主頁 >  其他 > Android開發深度學習的移動應用

Android開發深度學習的移動應用

2021-12-30 21:54:28 其他

Android配置tensorflow lite

按照官方網站的指導在專案的模塊的構建檔案build.gradle中配置中增加如下配置:

	implementation 'org.tensorflow:tensorflow-lite:2.7.0'
    implementation 'org.tensorflow:tensorflow-lite-gpu:2.7.0'
    implementation 'org.tensorflow:tensorflow-lite-support:0.1.0'
    implementation 'org.tensorflow:tensorflow-lite-metadata:0.1.0'

android{
   aaptOptions {
        noCompress "tflite"
    }
  defaultConfig {
        ndk {
            abiFilters 'armeabi-v7a', 'arm64-v8a'
        }
    }
 }

匯入模型資源資源

創建將文《關于將Tesorflow的SavedModel模型轉換成tflite模型》創建的模型model.tflite,匯入到Android專案的assets目錄中,

定義模型基本配置類BaseModelConfig

/**
 * 定義模型的基本配置類
 */
public abstract class BaseModelConfig{
    //每通道處理的位元組數
    var numBytesPerChannel:Int = 0
    //定義批處理的個數
    var dimBatchSize:Int = 0
    //定義像素個數
    var dimPixelSize:Int = 0
    //定義圖片的寬度
    var dimImgWidth:Int = 0
    //定義圖片的高度
    var dimImgHeight:Int = 0
    //定義平均差
    var imageMean=0
    //定義圖片的標準差
    var imageSTD:Float = 0.0F
    //定義模型的名稱
    lateinit var modelName:String

    constructor() : super() {
        setConfigs()
    }
    /**
     * 將像素值轉換成ByteBuffer
     * 增加圖片的值
     */
    public abstract fun addImgValue(buffer: ByteBuffer,pixel:Int)

    /**
     * 配置
     */
    public abstract fun setConfigs()
}

定義FloatSavedModelConfig類

class FloatSavedModelConfig: BaseModelConfig() {
    public override fun setConfigs() {
        modelName="model.tflite"
        numBytesPerChannel = 4
        dimBatchSize = 1
        dimPixelSize = 1
        dimImgWidth = 28
        dimImgHeight = 28
        imageMean = 0
        imageSTD = 255.0f
    }

    override fun addImgValue(imgData: ByteBuffer, pixel: Int) {
        imgData.putFloat(((pixel  and 0xFF) - imageMean) / imageSTD)
    }
}

創建配置模型引數的工廠類

object ModelConfigFactory {
    const val FLOAT_SAVED_MODEL = "float_saved_model"
    const val QUANT_SAVED_MODEL = "quant_saved_model"

    fun getModelConfig(model: String): BaseModelConfig? =
        when(model) {
            FLOAT_SAVED_MODEL-> FloatSavedModelConfig()
            QUANT_SAVED_MODEL-> QuantSavedModelConfig()
            else->null
        }
}

定義影像分類器

class ImageClassifier {
    private val TAG = "FashionMNIST"
    private val RESULTS_TO_SHOW = 3

    lateinit var mTFLite: Interpreter

    lateinit var mModelPath:String
    var mNumBytesPerChannel = 0

    var mDimBatchSize = 0
    var mDimPixelSize = 0

    var mDimImgWidth = 0
    var mDimImgHeight = 0

    lateinit var mModelConfig:BaseModelConfig

    //定義標簽檢測的二維陣列1x10
    val mLabelProbArray = Array(1) {
        FloatArray(
            10
        )
    }
    val labels = arrayListOf("T恤","褲子","帽頭衫","連衣裙","外套","涼鞋","襯衫","運動鞋","包","靴子")

    //定義檢測結果保持到優先佇列中
    var mSortedLabels = PriorityQueue<Map.Entry<String, Float>>(
                        RESULTS_TO_SHOW) {
            o1, o2 -> o1?.value!!.compareTo(o2?.value!!)
    }

    /**
     * 配置引數
     */
    private fun initConfig(config: BaseModelConfig) {
        mModelConfig = config
        mNumBytesPerChannel = config.numBytesPerChannel
        mDimBatchSize = config.dimBatchSize
        mDimPixelSize = config.dimPixelSize
        mDimImgWidth = config.dimImgWidth
        mDimImgHeight = config.dimImgHeight
        mModelPath = config.modelName
    }

    constructor(modelConfig: String, activity: Activity) {
        // 初始化分類器的相關引數
        initConfig(ModelConfigFactory.getModelConfig(modelConfig)!!)

        // 使用配置引數初始化翻譯器
        mTFLite = Interpreter(loadModelFile(activity)!!)
    }

    /**
     * 在Assets中的模型檔案映射到記憶體中
     * */
    private fun loadModelFile(activity: Activity): MappedByteBuffer? {
        val fileDescriptor = activity.assets.openFd(mModelPath)
        val inputStream = FileInputStream(fileDescriptor.fileDescriptor)
        val fileChannel = inputStream.channel
        val startOffset = fileDescriptor.startOffset
        val declaredLength = fileDescriptor.declaredLength
        return fileChannel.map(FileChannel.MapMode.READ_ONLY, startOffset, declaredLength)
    }

    /**
     * 將圖片資料寫入到ByteBuffer,加載到記憶體中
     * */
    protected fun convertBitmapToByteBuffer(bitmap: Bitmap?): ByteBuffer {
        val intValues = IntArray(mDimImgWidth * mDimImgHeight)
        //調整要處理的圖片為28x28
        var tmp = scaleBitmap(bitmap)
        //將圖片二值化
        tmp = binarized(tmp)

        //將二值化的圖片加載到記憶體中
        tmp.getPixels(intValues,
            0, tmp.width, 0, 0, tmp.width, tmp.height
        )
        val imgData = ByteBuffer.allocateDirect(
            mNumBytesPerChannel * mDimBatchSize * mDimImgWidth * mDimImgHeight * mDimPixelSize
        )
        imgData.order(ByteOrder.nativeOrder())
        imgData.rewind()

        //將圖片轉換成像素實數資料
        var pixel = 0
        for (i in 0 until mDimImgWidth) {
            for (j in 0 until mDimImgHeight) {
                var value = intValues[pixel++]
                mModelConfig.addImgValue(imgData, value)
            }
        }
        return imgData
    }

    /**
     * 將圖片二值化處理
     * 轉換成二值影像
     * @param bmp
     * @return
     */
    fun binarized(bmp: Bitmap): Bitmap {
        val width = bmp.width
        val height = bmp.height
        val pixels = IntArray(width * height)
        //將圖片的像素加載到陣列中
        bmp.getPixels(pixels, 0, width, 0, 0, width, height)
        var alpha = 0xFF shl 24
        for (i in 0 until height) {
            for (j in 0 until width) {
                val grey = pixels[width * i + j]
                // 分離三原色
                alpha = grey and -0x1000000 shr 24
                var red = grey and 0x00FF0000 shr 16
                var green = grey and 0x0000FF00 shr 8
                var blue = grey and 0x000000FF
                val tmp = 180
                red = if (red > tmp) 255 else 0
                blue = if (blue > tmp) 255 else 0
                green = if (green > tmp) 255 else 0
                pixels[width * i + j] = alpha shl 24 or (red shl 16) or (green shl 8) or blue
                if (pixels[width * i + j] == -1) {
                    pixels[width * i + j] = -1
                } else {
                    pixels[width * i + j] = -16777216
                }
            }
        }
        // 新建圖片
        val newBmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
        // 設定圖片資料
        newBmp.setPixels(pixels, 0, width, 0, 0, width, height)
        return newBmp
    }

    /**
     * 將圖片調整到規定的大小28x28
     */
    fun scaleBitmap(bmp: Bitmap?): Bitmap {
        return Bitmap.createScaledBitmap(bmp!!, mDimImgWidth, mDimImgHeight, true)
    }

    /**
     * 分類處理
     */
    fun doClassify(bitmap: Bitmap?): String? {
        // 將Bitmap圖片轉換成TFLite翻譯器的可讀的ByteBuffer
        val imgData = convertBitmapToByteBuffer(bitmap)

        // do run interpreter
        val startTime = System.nanoTime()
        mTFLite.run(imgData, mLabelProbArray)
        val endTime = System.nanoTime()
        Log.i(TAG, String.format(
                "運行識別的時間: %f ms",
                (endTime - startTime).toFloat() / 1000000.0f
            )
        )

        // 生成并回傳結果
        return printTopKLabels()
    }

    /**
     * 列印檢測排序在前幾位的標簽,并作為結果顯示在UI界面中,
     */
    fun printTopKLabels(): String? {
        for (i in 0..9) {
            mSortedLabels.add(
                AbstractMap.SimpleEntry(
                    labels[i],
                    mLabelProbArray[0][i]
                )
            )
            if (mSortedLabels.size > RESULTS_TO_SHOW) {
                mSortedLabels.poll()
            }
        }
        val textToShow = StringBuffer()
        val size = mSortedLabels.size
        for (i in 0 until size) {
            val label = mSortedLabels.poll()
            textToShow.insert(0, String.format("\n%s   %4.8f", label.key, label.value))
        }
        return textToShow.toString()
    }

}

定義主活動MainActivity

在主活動中,主要處理如下操作:
(1)從圖庫中選擇圖片
(2)利用影像分類器檢測圖片中的內容,判斷是FashionMnist資料集的哪種標簽
(3)將檢測的結果在移動終端的GUI界面中顯示出來,

class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding
    val RequestCameraCode = 1
    val TAG = "FashionMNIST"
    companion object{
        var mIsFloat = true
    }
    private var bitmap: Bitmap? = null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        //生成視圖系結物件
        binding = ActivityMainBinding.inflate(layoutInflater)
        //設定視圖的根視圖
        setContentView(binding.root)

        binding.imageView.setOnClickListener {
            val intent = Intent()
            intent.type = "image/*"
            intent.action = Intent.ACTION_GET_CONTENT
            startActivityForResult(intent,RequestCameraCode)
        }

        val spinnerAdapter = ArrayAdapter<String>(this,android.R.layout.simple_spinner_item,getChoices())
        binding.typeSpinner.adapter = spinnerAdapter
        
        binding.typeSpinner.onItemSelectedListener = object : OnItemSelectedListener {
            override fun onItemSelected(
                parent: AdapterView<*>?,
                view: View,
                position: Int,
                id: Long
            ) {
                mIsFloat = position == 0
            }

            override fun onNothingSelected(parent: AdapterView<*>?) {}
        }
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if(resultCode == RESULT_OK && requestCode == RequestCameraCode){
            val uri = data?.data
            try{
                //從圖庫中讀取圖片
                var bitmap = BitmapFactory.decodeStream(contentResolver.openInputStream(uri!!))
                //在影像視圖ImageView中顯示圖片
                binding.imageView.setImageBitmap(bitmap)
                //判斷模型型別
                val config = when(mIsFloat){
                    true->ModelConfigFactory.FLOAT_SAVED_MODEL
                    else->ModelConfigFactory.QUANT_SAVED_MODEL
                }
                //根據模型型別創建影像識別器
                val classifier = ImageClassifier(config,this)
                //檢測并判斷影像的類別
                val result = classifier.doClassify(bitmap)
                binding.labelTxt.text = result
                binding.tipTxt.visibility = View.GONE
            }catch(e: FileNotFoundException){
                Log.d(TAG,"沒有找到指定的影像檔案")
            }catch(e: IOException){
                Log.e(TAG,"初始化影像識別器失敗")
            }

        }
    }
    /**
     * 回傳可用模型的名稱
     */
    private fun getChoices()= resources.getStringArray(R.array.model_names)

}

參考文獻

李錫涵等 《簡明的Tensorflow 2》人民郵電出版社 北京 P91-P96

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/398038.html

標籤:AI

上一篇:video播放視頻

下一篇:uniapp APP端使用web-view加載一個H5頁面,跳轉回APP指定頁面

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more