Android OpenCV (一) 基礎API 清晰度亮度識別

語言: CN / TW / HK

背景

  工作中遇到業務訴求是通過OpenCV對圖片進行一些判斷操作和優化,這裏是看了部分不錯的文章,希望總結一個自己的學習過程,温故而知新,有不對的地方可以評論區指出,小白學習海涵。

基礎知識

Mat在OpenCV中是非常重要的存在,後續各個API都是在Mat的基礎上去做文章,Mat 是Matrix(矩陣)的縮寫

```c++ ... inline Mat::Mat(int _rows, int _cols, int _type) : flags(MAGIC_VAL), dims(0), rows(0), cols(0), data(0), datastart(0), dataend(0), datalimit(0), allocator(0), u(0), size(&rows), step(0) { create(_rows, _cols, _type); }

inline void Mat::create(int _rows, int _cols, int _type) { _type &= TYPE_MASK; if( dims <= 2 && rows == _rows && cols == _cols && type() == _type && data ) return; int sz[] = {_rows, _cols}; create(2, sz, _type); }

...

``` Mat中其實保存着關於圖片的圖像信息,包括像素、寬、高、類型大小深度等屬性。

主要Api - 加載圖片

  由於我這裏使用的集團的二方庫,讀者大佬可以直接在github搜索 OpenCV對應版本,在gradle中添加依賴即可,由於本人是使用的Java代碼通過jni調用底層C++代碼,大部分api其實是互通的(網上Python教程居多,這也是我想把這個過程總結下來的原因)

imread

  該方法主要是獲取圖片的Mat信息的 默認通道為BGR(Blue, Green, Red),可以有很多flags供我們選擇以此達到不同的效果。 ``` public static Mat imread(String filename, int flags) { return new Mat(imread_0(filename, flags)); }

// 調用例子 eg: Mat bgr = Imgcodecs.imread(filePath, Imgcodecs.IMREAD_UNCHANGED); Imgproc.cvtColor(bgr, srcMat, Imgproc.COLOR_BGR2RGB);

//Imgcodecs flags 常用參數含義 public static final int IMREAD_UNCHANGED = -1; // 無改動 public static final int IMREAD_GRAYSCALE = 0; // 單通道灰色 public static final int IMREAD_COLOR = 1; //三通道BGR圖像

```

Utils.bitmapToMat

  通過Utils.bitmapToMat方法獲取Mat對象。 ```

Bitmap bitmap = BitmapFactory.decodeResource(res, R.drawable.icon); Mat mat = Mat() // bitmap : 支持ARGB_8888和RGB_565兩種格式 // mat : 類型為CV_8UC4,通道順序為RGBA Utils.bitmapToMat(bitmap, mat); ```

主要API - 寫入圖片

   我們可以通過 imwrite方法將Mat對象保存至指定文件

java File file = new File(Environment.getExternalStorageDirectory().getPath() + File.separator + "${System.currentTimeMillis()}.jpg"); if (!file.exists()) { file.createNewFile(); } // 文件路徑 , 待輸出mat對象 Imgcodecs.imwrite(file.getPath(), srcMat);

端側常用分析方法

亮度檢測

  計算圖片在灰度圖上的均值和方差,當存在亮度異常時,均值會偏離均值點(可以取0-255中間值 128),方差也會偏小;通過計算灰度圖的均值和方差,就可評估圖像是否存在過曝光或曝光不足。 //亮度檢測 private static float brightness(Mat grayImage) { float a = 0; int Hist[] = new int[256]; for (int i = 0; i < 256; i++) { Hist[i] = 0; } for (int i = 0; i < grayImage.rows(); i++) { for (int j = 0; j < grayImage.cols(); j++) { //在計算過程中,考慮128為亮度均值點 a += (float) (grayImage.get(i, j)[0] - 128); int x = (int) grayImage.get(i, j)[0]; Hist[x]++; } } float da = a / (float) (grayImage.rows() * grayImage.cols()); float D = Math.abs(da); float Ma = 0; for (int i = 0; i < 256; i++) { Ma += Math.abs(i - 128 - da) * Hist[i]; } Ma /= (float) ((grayImage.rows() * grayImage.cols())); float M = Math.abs(Ma); float K = D / M; float cast = K; if (cast >= 1) { if (da > 0) { Log.e("ymc", "過亮"); } else { Log.e("ymc", "過暗"); } } else { Log.e("ymc", "亮度:正常"); } return cast; }

清晰度檢測

  利用拉普拉斯算子計算圖片的二階導數,反映圖片的邊緣信息,同樣事物的圖片,清晰度高的,相對應的經過拉普拉斯算子濾波後的圖片的方差也就越大。 //清晰度 private static double clarity(Mat grayImage) { Mat laplacianDstImage = new Mat(); Imgproc.Laplacian(grayImage, laplacianDstImage, CvType.CV_64F); MatOfDouble median = new MatOfDouble(); MatOfDouble std = new MatOfDouble(); Core.meanStdDev(laplacianDstImage, median, std); double clarity = Math.pow(std.get(0, 0)[0], 2); //後續可根據業務設置閾值 Log.e("ymc", "清晰度:" + clarity); laplacianDstImage.release(); return clarity; }

最後

  2022年接觸了很多新東西,在工作中也看到了很多大佬的閃光點,後續還會有更深入的OpenCV使用案例博文,圖片分析方面還是菜鳥,這篇文章也看了很多Python大佬的文章,正所謂三人行必有我師,繼續學習。