内容提要:
图像深度
:表示每个通道灰度值所占的大小
,大小即占几个位,是什么数据类型
;
图像类型
:基于图像深度表述的信息
,多了一个尾缀Cx
,表示图像的通道数
是x
个;
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
表示无符号char; -
3
表示三个通道。
在如上的七行类型表中,每个类型都可以做类似的解读;
也可以看出CV_8U就是图像深度
,所以图像类型与深度之间是有直接关系
的。
1.2 Mat创建与初始化
- 综上,Mat对象中包含了图像的各种基本信息与图像像素数据;
-
Mat
是由头部
与数据部分
组成的,其中头部还包含一个指向数据的指针
。 - 在OpenCV4Android的接口封装中,因为Java层面没有
指针对象
,因此全部用数组
来替代; - 但是,当我们需要把
Mat对象
传到JNI层
的时候,
可以通过getNativeObjAddr()
方法来实现Mat对象从Java层
到C++层
的指针传递
;
-
创建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