OpenCV | 詳析Mat與Bitmap物件(建立、初始化、使用與轉換 | 附大量demo)
1. Mat物件
-
Mat
是OpenCV中用來儲存影象資訊的記憶體物件
; - 當通過
Imgcodecs.imread()
方法從檔案讀入一個影象檔案時,imread方法就會返回Mat
物件例項; - 或者通過
Utils.bitmatToMat()
方法由Bitmap物件轉換得到Mat
物件例項。
下圖形象地展示了一張影象中的各個畫素點資料是如何儲存的,
因為影象本身的畫素點比較多,下圖顯示的影象畫素資料只是圖片左上角20×20大小的部分資料:

1.1 載入圖片與讀取基本資訊
-
從Android系統中選擇一張影象
時,可以使用如下程式碼將影象檔案載入為Mat物件:
Mat src = Imgcodecs.imread(fileUri.getPath());
-
OpenCV通過imread來載入影象,
預設載入
的是三通道順序為BGR
的彩色影象;還可以通過以下程式碼來指定載入為彩色影象:
(比上一句多了第二個引數)
Mat src = Imgcodecs.imread(fileUri.getPath(), Imgcodecs.IMREAD_COLOR)
如上這句程式碼,
第一個引數表示檔案路徑;
第二個引數表示 載入影象型別
,最常見的型別有如下幾種:
- IMREAD_UNCHANGED= -1,表示
不改變載入影象型別
,可能包含透明通道
。 - IMREAD_GRAYSCALE= 0,表示載入影象為
灰度影象
。 - IMREAD_COLOR= 1,表示載入影象為
彩色影象
。
使用如下程式碼從 Mat物件
中得到影象的 寬、高、維度、通道數、深度、型別資訊
:
int width = src.cols(); int height = src.rows(); int dims = src.dims(); int channels = src.channels(); int depth = src.depth(); int type = src.type();
其中要特別關注 通道數、影象深度與影象型別、OpenCV載入的Mat型別影象物件
。
-
常見的通道數目
有1、3、4
,分別對應於單通道、三通道、四通道
,其中四通道
中通常會有透明通道的資料
; -
影象深度
表示每個通道灰度值所佔的大小
,影象深度與型別密切相關;
OpenCV中常見的幾種影象深度
: -
U
表示無符號整型
; -
S
表示符號整型
; -
F
表示浮點數
;
這些型別在CvType
中可以自己檢視。OpenCV中常見的影象型別
如下:
當呼叫 imread
函式時,
如果只使用檔案路徑引數讀入載入一張影象,則 預設值
是 三通道
的 CV_8UC3
, 影象深度
為 CV_8U
,
其中:
CV 8 UC 3
在如上的七行型別表中,每個型別都可以做類似的解讀;
也可以看出 CV_8U就是影象深度
,所以 影象型別與深度之間是有直接關係
的。
1.2 Mat建立與初始化
- 綜上,Mat物件中包含了影象的各種基本資訊與影象畫素資料;
-
Mat
是由頭部
與資料部分
組成的,其中頭部還包含一個指向資料的指標
。 - 在OpenCV4Android的介面封裝中,因為Java層面沒有
指標物件
,因此全部用陣列
來替代; - 但是,當我們需要把
Mat物件
傳到JNI層
的時候,
可以通過getNativeObjAddr()
方法來實現Mat物件從Java層
到C++層
的指標傳遞
;
如圖是Mat在記憶體中的結構:

-
建立Mat物件的方法
有很多種,如下幾種最常見:1)通過
create
方法建立:
Mat m1 = new Mat(); m1.create(new Size(3, 3), CvType.CV_8UC3); Mat m2 = new Mat(); m2.create(3, 3, CvType.CV_8UC3);
上述程式碼建立兩個Mat物件—— m1
與 m2
,它們的大小都是3×3、型別都是三通道8位的無符號字元型。
2)通過 ones、eye、zeros
方法初始化建立:
Mat m3 = Mat.eye(3, 3,CvType.CV_8UC3); Mat m4 = Mat.eye(new Size(3, 3),CvType.CV_8UC3); Mat m5 = Mat.zeros(new Size(3, 3), CvType.CV_8UC3); Mat m6 = Mat.ones(new Size(3, 3), CvType.CV_8UC3);
上述程式碼建立了 m3、m4、m5、m6
四個Mat物件,基於這種初始化方式來得到Mat物件是OpenCV借鑑了 Matlab
中 eye、zeros、ones
三個函式實現的。
3)先定義Mat,然後通過 setTo
方法實現初始化:
Mat m7 = new Mat(3, 3, CvType.CV_8UC3); m7.setTo(new Scalar(255, 255, 255));
此方法與第一種方法有點類似,區別在於第一種方法通過 create
初始化時沒有指定 顏色值
。
在OpenCV中, 顏色向量
通常用 Scalar
表示,這裡 Scalar(255,255,255)
表示 白色
。
4)通過Mat的 copyTo()
與 clone()
實現物件的建立,
Mat中的克隆與拷貝方法會複製一份完全相同的資料以建立一個新的Mat物件,
克隆相關程式碼如下:
Mat m8 = new Mat(500, 500, CvType.CV_8UC3); m8.setTo(new Scalar(127, 127, 127)); Mat cmat = image.clone();
拷貝的相關程式碼如下:
at m8 = new Mat(500, 500, CvType.CV_8UC3); m8.setTo(new Scalar(127, 127, 127)); Mat result = new Mat(); m8.copyTo(result)
1.3 Mat物件儲存
建立
好的Mat物件經過一系列的 操作
之後,就可以通過OpenCV4Android的 imwrite
函式直接將物件 儲存
為影象:
// 建立Mat物件並儲存 Mat image = new Mat(500, 500, CvType.CV_8UC3); image.setTo(new Scalar(127, 127, 127)); ImageSelectUtils.saveImage(image);
其中:
500
表示影象的寬度與高度, vType.CV_8UC3
宣告影象是RGB彩色三通道影象、每個通道都是 8
位;
第二行程式碼是指定影象的每個畫素點、每個通道的灰度值為 127
;
第三行程式碼是使用 imwrite
將影象儲存到手機中的指定目錄下;
saveImage方法內容如下:
File fileDir = new File(Environment.getExternalStoragePublicDirectory( Environment.DIRECTORY_PICTURES), "mybook"); if(!fileDir.exists()) { fileDir.mkdirs(); } String name = String.valueOf(System.currentTimeMillis()) + "_book.jpg"; File tempFile = new File(fileDir.getAbsoluteFile()+File.separator, name); Imgcodecs.imwrite(tempFile.getAbsolutePath(), image);
上面的前幾行程式碼是 建立目錄與檔案路徑
,
最後一行程式碼通過 imwrite
來實現 檔案的儲存
,
儲存影象的格式取決於檔案路徑為影象指定的副檔名型別(如程式碼中的.jpg)。
2. Android中的Bitmap物件
其實Android系統中有一個與 Mat
物件相似的物件 Bitmap
。
通過它可以 獲取影象的常見屬性、畫素資料,修改影象的畫素資料,呈現出不同的影象顯示效果,儲存影象
,等等。
2.1 影象檔案與資源載入
在Android系統中,
可以把 給定影象的檔案路徑
或者 影象資源ID作為引數
,
通過呼叫API來實現檔案載入,使 目標圖片
成為一個 Bitmap
例項物件。
最常見的載入資源影象的方法:
Bitmap bm = BitmapFactory.decodeResource(this.getResources(), R.drawable.lena);
載入影象檔案時,為了避免OOM問題,
- 首先應該獲取影象的大小,
- 然後根據影象大小進行適當的降取樣,
- 之後再載入為Bitmap物件:
private void displaySelectedImage() { if(fileUri == null) return; ImageView imageView = (ImageView)this.findViewById(R.id.sample_img); BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(fileUri.getPath(), options); int w = options.outWidth; int h = options.outHeight; int inSample = 1; if(w > 1000 || h > 1000) { while(Math.max(w/inSample, h/inSample) > 1000) { inSample *=2; } } options.inJustDecodeBounds = false; options.inSampleSize = inSample; options.inPreferredConfig = Bitmap.Config.ARGB_8888; Bitmap bm = BitmapFactory.decodeFile(fileUri.getPath(), options); imageView.setImageBitmap(bm); }
2.2 讀寫畫素
對Bitmap物件,首先可以通過相關的API查詢到影象的長、寬、配置資訊;
在Bitmap中,畫素資料是最佔記憶體的部分;
根據長、寬與配置資訊可以計算出影象畫素的大小為多少;
讀取畫素時,
- 可以定義一個數組用於儲存一次性讀出的畫素陣列;
- 也可以通過每次讀取一個畫素點的方式來迴圈讀取。
Bitmap獲取影象寬、高與配置資訊的介面程式碼如下:
public final int getWidth() public final int getHeight() public final Config getConfig()
其中, Config
是Java中的 列舉型別
,
當前Android支援的 Bitmap畫素儲存型別
具體如下:
Bitmap.Config.ALPHA_8; Bitmap.Config.ARGB_4444; Bitmap.Config.RGB_565; Bitmap.Config.ARGB_8888;
預設情況下,Bitmap是在RGB色彩空間。
其中:
- A表示透明通道;
- R表示紅色通道;
- G表示綠色通道;
- B表示藍色通道。
其中 ALPHA_8
表示該影象只有 透明通道
而沒有 顏色通道
,是一張透明通道影象,
這種影象通常會被用作mask影象。
上述程式碼引數具體分析如下:
·ARGB_4444:表示每個通道佔四位,總計兩個位元組,表示一個畫素的影象。 ·ARGB_8888:表示每個通道佔八位,總計四個位元組,表示一個畫素的影象,這個是最常見的。 ·ARGB_565:表示每個通道分別佔5位、6位、5位,總計兩個位元組,表示一個畫素的影象。
在Bitmap中迴圈 讀取每個畫素每個通道
並 修改
的程式碼如下:
public void getBitmapInfo() { Bitmap bm = BitmapFactory.decodeResource(this.getResources(), R.drawable.lena); int width = bm.getWidth(); int height = bm.getHeight(); Bitmap.Config config = bm.getConfig(); int a=0, r=0, g=0, b=0; for(int row=0; row<height; row++) { for(int col=0; col<width; col++) { // 讀取畫素 int pixel = bm.getPixel(col, row); a = Color.alpha(pixel); r = Color.red(pixel); g = Color.green(pixel); b = Color.blue(pixel); // 修改畫素 r = 255 - r; g = 255 - g; b = 255 - b; // 儲存到Bitmap中 bm.setPixel(col, row, Color.argb(a, r, g, b)); } }
這種方式每次只 讀取
一個畫素點的顏色值,然後 修改
並 設定
的方法,
會造成對Bitmap物件的 頻繁訪問,效率低下
。
在 DVM記憶體不緊張
的時候,應該選擇:
-
開闢一塊畫素緩衝區
, -
一次性讀取全部畫素作為陣列
, - 然後
迴圈陣列
,訪問
每個畫素點, -
修改
完成之後再重新設回
Bitmap對應的畫素資料中,
這種方法 速度很快
,也更為 常見
。
實現程式碼如下:
private void scanPixelsDemo() { Bitmap bm = BitmapFactory.decodeResource(this.getResources(), R.drawable.lena).copy(Bitmap.Config.ARGB_8888, true); int width = bm.getWidth(); int height = bm.getHeight(); Bitmap.Config config = bm.getConfig(); int[] pixels = new int[width*height]; bm.getPixels(pixels, 0, width, 0, 0, width, height); int a=0, r=0, g=0, b=0; int index = 0; for(int row=0; row<height; row++) { for(int col=0; col<width; col++) { // 讀取畫素 index = width*row + col; a=(pixels[index]>>24)&0xff; r=(pixels[index]>>16)&0xff; g=(pixels[index]>>8)&0xff; b=pixels[index]&0xff; // 修改畫素 r = 255 - r; g = 255 - g; b = 255 - b; // 儲存到Bitmap中 pixels[index] = (a << 24) | (r << 16) | (g << 8) | b; } } bm.setPixels(pixels, 0, width, 0, 0, width, height); ImageView iv = (ImageView)this.findViewById(R.id.matInfo_imageView); iv.setImageBitmap(bm); bm.recycle(); }
關於上述程式碼讀取儲存部分程式碼的補充解析:
-
初定義時,
itmap.Config.ARGB_8888
,也即每個畫素點有8 * 4 = 32個bit
;其中ARGB四個通道各用8個bit表示,依序排列;
-
0xff
,剛好為8bit
,因為每位16進位制數等於4位bit
; -
類似
a=(pixels[index]>>24)&0xff;
般右移
操作,意義在於
擷取
原8 * 4 = 32
個bit中各自的8bit有效位
,接著同0xff
相與
,1位為1保留,0位為0保留; -
各個通道各自的8bit有效位
計算(修改)
完畢之後,再將各通道相與結果
左移
對應的位數,最後統一
相或
,則得到修改後的一個畫素點的32個bit
;
2.3 釋放記憶體
-
建立
與使用Bitmap
物件完成讀寫
畫素資料操作之後,需要呼叫
bm.recycle()
釋放已經不再需要使用Bitmap物件的記憶體空間; -
對建立的Mat物件來說,當使用完之後,需要呼叫
release()
來釋放記憶體,否則在進行批量影象處理或者視訊處理時,
會很容易因為Mat物件的大量建立而不釋放導致記憶體問題與APP崩潰。
3. 基礎形狀繪製與填充
- 使用OpenCV做
物件檢測、物件識別
程式開發,很多場景下,需要在輸出影象上
對處理結果
加上醒目的輪廓
或者以邊框矩形繪製
或者顏色填充
,這個就需要學會圖形繪製相關API的使用。 - 常見的繪製包括矩形、圓形、橢圓、直線、還有文字文字。
- 無論是
Android Canvas
還是OpenCV SDK
,它們本身都已經提供了這些簡單繪製API的支援。
3.0首先是OpenCV是在Mat影象上繪製與填充
OpenCV2.xAndroid SDK
圖形繪製是在 Core
模組中,
到了 OpenCV3.x
中,圖形繪製就已經移到 Imgproc
這個模組中了。
3.1 在Mat上繪製基本幾何形狀與文字
Mat
上繪製的基本幾何形狀包括 矩形、直線、圓、橢圓
,還有 文字文字
。
下面是繪製這幾個形狀 相關的API
說明:
-
line(Mat img,Point pt1,Point pt2,Scalar color,int thickness,int lineType,int shift)
表示繪製直線
,
-
最後三個引數
可以不填,預設值
分別為1、8、0
,表示繪製寬度
是1個畫素、繪製方法
是8鄰域、位置偏移
為0;
後面的API方法若無特別解釋,則最後的這三個引數基本含義都是一樣的。
-
前面的四個引數
分別解釋如下:
img
:傳入一個Mat物件,表示繪製物件是在Mat影象上,後面幾個API方法同理
。
pt1
:表示直線起始點
的螢幕座標。
pt2
:表示直線終點
的螢幕座標。
color
:表示直線的顏色
,假設三通道的順序為BGR,則new Scalar(0,0,255)表示紅色。
-
rectangle(Mat img,Point pt1,Point pt2,Scalar color,int thickness,int lineType,int shift)
繪製矩形跟繪製直線的方法引數極其類似,主要是兩個座標點引數含義不一樣:
pt1
:表示矩形左上角點
的螢幕座標;pt2
:表示矩形右下角點
的螢幕座標;
-
circle(Mat img,Point center,int radius,Scalar color,int thickness,int lineType,int shift)
img
:同上。center
:表示圓的中心點
位置的螢幕座標
,單位是畫素
。radius
:表示圓的半徑大小
,單位是畫素
。color
:表示圓的顏色
。
-
ellipse(Mat img,Point center,Size axes,double angle,double startAngle,double endAngle,Scalar color,int thickness,int lineType,int shift)
-
繪製橢圓與上述API相比多了幾個引數,繪製
橢圓
或者弧長
的時候需要指定開始與結束的角度
,長軸與短軸大小
、中心位置
等資訊;img
:同上;center
:表示橢圓的中心位置點
螢幕座標。axes
:表示橢圓的長軸
與短軸
大小,單位是畫素
;需傳入的是一個Size資料對,如new Size(100, 50);
angle
:表示旋轉角度
,通常angle = endAngle – startAngle
。startAngle
:開始角度
大小。endAngle
:結束角度
大小。color
:表示橢圓的顏色
。
-
putText(Mat img,String text,Point org,int fontFace,double fontScale,Scalar color,int thickness)
-
表示在Mat影象上繪製文字文字,
OpenCV
的預設情況是不支援中文文字繪製顯示
的,如果想要顯示中文資訊,可以切換
到Bitmap
物件然後繪製
;img
:同上。text
:表示要顯示的文字
。org
:表示開始位置點
螢幕座標。fontFace
:表示字型型別
。fontScale
:表示字型大小
。color
:表示文字的顏色
。thickness
:表示文字繪製的寬度,預設大小為1。
另外補充:
-
OpenCV會根據
thickness的值
來決定是進行填充
還是隻做描邊繪製
;在上述矩形、圓、橢圓的繪製方法中,如果想要把繪製方式改為
填充
,只需要設定引數thickness=-1
即可; -
引數
lineType
則表示繪製線段型別
,預設情況下是8,表示八鄰域繪製方式;lineType
共有三種方式分別如下。
·LINE_4:表示繪製線段的時候使用四鄰域填充方法。 ·LINE_8:表示繪製線段的時候使用八鄰域填充方法。 ·LINE_AA:表示繪製線段的時候使用反鋸齒填充方法。
下面建立一個 500×500px
大小的Mat物件,型別是 CV_8UC3
,
然後在上面的API實際操作練習一下:
private void basicDrawOnMat() { //建立Mat物件 Mat src = Mat.zeros(500, 500, CvType.CV_8UC3); //開始繪製 Imgproc.ellipse(src, new Point(250, 250), new Size(100, 50), 360, 0, 360, new Scalar(0, 0, 255), 2, 8, 0); Imgproc.putText(src, "Basic Drawing Demo", new Point(20, 20), Core.FONT_HERSHEY_PLAIN, 1.0, new Scalar(0, 255, 0), 1); Rect rect = new Rect(); rect.x = 50; rect.y = 50; rect.width = 100; rect.height = 100; Imgproc.rectangle(src, rect.tl(), rect.br(), //矩形 new Scalar(255, 0, 0), 2, 8, 0); Imgproc.circle(src, new Point(400, 400), 50, new Scalar(0, 255, 0), 2, 8, 0); Imgproc.line(src, new Point(10, 10), new Point(490, 490), new Scalar(0, 255, 0), 2, 8, 0); Imgproc.line(src, new Point(10, 490), new Point(490, 10), new Scalar(255, 0, 0), 2, 8, 0); //繪製完畢 //建立一個同Mat一樣大小Bitmap物件 Bitmap bm = Bitmap.createBitmap(src.cols(), src.rows(), Bitmap.Config.ARGB_8888); Mat dst = new Mat();//準備一個Mat緩衝變數 Imgproc.cvtColor(src, dst, Imgproc.COLOR_BGR2RGBA);//把三通道的Mat物件(即src)轉化成四通道的Mat物件賦到dst上 Utils.matToBitmap(dst, bm);//dst轉換成Bitmap物件 ImageView iv = (ImageView)this.findViewById(R.id.matInfo_imageView); iv.setImageBitmap(bm); }
執行效果如下:

3.2 在Canvas上繪製基本幾何形狀與文字
Android中在 Bitmap
上繪製 幾何形狀與文字物件
,要藉助 Canvas
相關API實現;
- 首先準備好一個
Bitmap物件
; - 再用準備好的
Bitmap物件
作為建構函式的引數
構造出一個Canvas物件
, - 然後使用Canvas的繪製API完成
顏色
與風格
的設定, - Canvas繪製顏色與風格設定都是通過
Paint
物件來完成的;
像這樣首先建立Paint例項,然後設定顏色與風格:
Paint p = new Paint(); p.setColor(Color.GREEN); p.setStyle(Paint.Style.STROKE)
常見的 風格
還包括如下幾種:
·Paint.Style.STROKE:描邊。 ·Paint.Style.FILL:填充。 ·Paint.Style.FILL_AND_STROKE:填充與描邊。
設定好 Paint
之後就可以開始繪製了:
// 繪製直線 canvas.drawLine(10, 10, 490, 490, p); canvas.drawLine(10, 490, 490, 10, p); // 繪製矩形 android.graphics.Rect rect = new android.graphics.Rect(); rect.set(50, 50, 150, 150); // 矩形左上角點,與右下角點座標 canvas.drawRect(rect, p); // 繪製圓 p.setColor(Color.GREEN); canvas.drawCircle(400, 400, 50, p); // 繪製文字 p.setColor(Color.RED); canvas.drawText("Basic Drawing on Canvas", 40, 40, p);
繪製方法全文( canvas繪製的內容都會對映在繫結的Bitmap上
):
private void basicDrawOnCanvas() { // 建立Bitmap物件 Bitmap bm = Bitmap.createBitmap(500, 500, Bitmap.Config.ARGB_8888); // 建立畫布與畫筆風格 Canvas canvas = new Canvas(bm); Paint p = new Paint(); p.setColor(Color.BLUE); p.setStyle(Paint.Style.FILL_AND_STROKE); // 繪製直線 canvas.drawLine(10, 10, 490, 490, p); canvas.drawLine(10, 490, 490, 10, p); // 繪製矩形 android.graphics.Rect rect = new android.graphics.Rect(); rect.set(50, 50, 150, 150); // 矩形左上角點,與右下角點座標 canvas.drawRect(rect, p); // 繪製圓 p.setColor(Color.GREEN); canvas.drawCircle(400, 400, 50, p); // 繪製文字 p.setColor(Color.RED); canvas.drawText("Basic Drawing on Canvas", 40, 40, p); // 顯示結果 ImageView iv = (ImageView)this.findViewById(R.id.matInfo_imageView); iv.setImageBitmap(bm); bm.recycle();//!!!!!!!!!!!!!!!!!!!!!!!!!! }
綜上,
Android中提供的基於 Canvas的API
完整地實現了圖形繪製功能,
當用OpenCV在Android中做開發時,若需繪製 複雜的幾何圖形或中文文字
,
優先選擇本地 Canvas API
來完成。
4. Mat與Bitmap的使用與轉換
- 在Android中使用OpenCV來完成應用開發時經常需要在
Mat物件
與Bitmap物件
之間相互切換
; -
Bitmap
是Android
中的影象物件
,Ma
t作為OpenCV
中表示影象的記憶體容器
;
4.1 Mat與Bitmap相互轉換
第一種情況:
- 通過
影象物件通道
,即OpenCV的imread()
讀取得到Mat物件
; - 或者通過
Mat類
初始化建立的Mat物件
;
將這樣的 Mat物件
轉換為 Bitmap物件
的情況;
可以參考以下例項程式碼處理這種情況:
private void mat2BitmapDemo(int index) { Mat src = Imgcodecs.imread(fileUri.getPath());//通過imread讀取返回的Mat物件 int width = src.cols(); int height = src.rows(); Bitmap bm = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);將```影象Bitmap```載入為```ARGB_8888```方式, Mat dst = new Mat();//準備一個Mat緩衝變數 Imgproc.cvtColor(src, dst, Imgproc.COLOR_BGR2RGBA);//把三通道的Mat物件(即src)轉化成四通道的Mat物件賦到dst上 Utils.matToBitmap(dst, bm);//dst轉換成Bitmap物件 dst.release();//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ImageView iv = (ImageView)this.findViewById(R.id.matInfo_imageView); iv.setImageBitmap(bm); }
其中:
-
Utils.matToBitmap()
來自OpenCV4Android SDK
的Util
包,包中還有另外一個與它相對應的方法
Utils.bitmapToMat()
,通過它們就可以實現
Bitmap與Mat的相互轉換
。 -
Bitmap的型別
是ARGB_8888
,而
OpenCV
中載入影象預設的型別
為BGR
,所以需要通過
cvtColor()
轉換為RGBA四通道影象
之後,再呼叫mat與Bitmap的相互轉換方法(
matToBitmap()
)。
否則的出現 通道順序不正確
,會導致 影象顯示顏色異常
。
第二種情況更為 常見
:
通常地,
- 通過Android本地的API
建立
或者初始化載入影象
為Bitmap物件
;
(為簡化起見,《OpenCV Android 開發實戰》一書中預設載入Bitmap物件型別為ARGB_8888), -
Bitmap物件
傳遞到OpenCV
中轉換為Mat物件
; - 處理完成之後再將這
Mat物件
重新轉回Bitmap物件
; - 最後通過ImageView顯示。
可以參考以下例項程式碼處理這種情況:
private void bitmap2MatDemo() { Bitmap bm = Bitmap.createBitmap(500, 500, Bitmap.Config.ARGB_8888);//將```影象Bitmap```載入為```ARGB_8888```方式, Mat m = new Mat(); Utils.bitmapToMat(bm, m); Imgproc.circle(m, new Point(m.cols()/2, m.rows()/2), 50, new Scalar(255, 0, 0, 255), 2, 8, 0); Utils.matToBitmap(m, bm); ImageView iv = (ImageView)this.findViewById(R.id.matInfo_imageView); iv.setImageBitmap(bm); }
4.2 記憶體與顯示
- 在Android系統中,將
影象資原始檔直接載入為OpenCV中的Mat物件
,可以避免Bitmap載入大影象
出現的OOM問題
; - 使用
Mat物件
對影象完成操作
之後,所有的臨時Mat物件
都應該呼叫release()釋放記憶體
,
避免在JNI層面
發生記憶體洩漏
問題;
示例程式碼:
Mat dst = new Mat(); Imgproc.cvtColor(src, dst, Imgproc.COLOR_BGR2RGBA); Utils.matToBitmap(dst, bm); dst.release();//及時釋放臨時Mat物件記憶體空間
4.3 通道數、通道順序與透明通道問題
(1)預設通道數與順序
使用OpenCV4Android SDK 建立影象的時候
最好將其 指定為三通道預設的BGR順序
,
這也是 OpenCV載入影象檔案
為 Mat物件
的時候使用的 預設通道數與通道順序
。
(2)透明通道
- 在OpenCV中做影象處理,如果需要處理
透明通道
,則需要將影象Bitmap
載入為ARGB_8888
方式,
(如以上4.1 例子中的建立Bitmap時的程式碼
) - 然後轉換為
Mat物件
,此時Mat物件為四通道
,含有透明通道資料, - 這樣就可以進行透明通道混合等
操作
了, - 完成操作以後再通過Utils包中的方法
轉換回
Bitmap物件即可。
(3)灰度與二值影象
- 當
Mat
為灰度
或者二值影象
的時候, - 需要首先通過
cvtColor
指定轉換型別
為COLOR_GRAY2RGBA
, - 之後才可以把
Mat物件
轉換為Bitmap影象
。
參考資料
- 《OpenCV Android 開發實戰》(賈志剛 著)
- 關於本書作者的GitHub專案
- 基於作者GitHub維護的APP