opencv4android常用变换(二)

来源:互联网 发布:网络修真小说研究 编辑:程序博客网 时间:2024/06/03 17:47

opencv4android常用变换(二)

这是opencv4android的第二篇,之前的写的反响不错,不过有点少,本来上一篇中提到的那些函数应该在上一篇中都介绍的,不过被那个双边滤波器搞得脑子有点乱,就写了那么多,这一篇接着上一篇继续写。

图像金字塔

一个图像金字塔是一系列图像的集合 - 所有图像来源于同一张原始图像 - 通过梯次向下采样获得,直到达到某个终止条件才停止采样。
有两种类型的图像金字塔常常出现在文献和应用中:
高斯金字塔(Gaussian pyramid): 用来向下采样
拉普拉斯金字塔(Laplacian pyramid): 用来从金字塔低层图像重建上层未采样图像
在这篇文档中我们将使用 高斯金字塔 。–引至opencv教程。
说白了图像金字塔就是实现放大和缩小图片的一种方式。在android中实现图片的放大和缩小有好多方式,这里先不考虑各种实现方式的优缺点,先学会怎么用opencv实现缩放图像吧。
用到的函数:pyrDown(Mat src, Mat dst, Size dstsize)pyrUp(Mat src, Mat dst, Size dstsize),看起来几乎是一样的,也就方法名不一样,参数:原图像,输出图像,输出的大小。
我也不理解为什么这里的size就是输出后的大小而不是内核的意思。整体代码:

//缩小图像private void changeBitmap() {    if(rgbMat==null|dstMat==null){    rgbMat=new Mat();    dstMat=new Mat();    Utils.bitmapToMat(mBitmap,rgbMat);    }    Imgproc.pyrDown(rgbMat,dstMat,new Size(rgbMat.cols()/2,rgbMat.rows()/2));    rgbMat=dstMat;    Utils.matToBitmap(dstMat,dstBitmap);    mImage.setImageBitmap(dstBitmap);}//放大图像private void grayBitmap(){    if(rgbMat==null|dstMat==null){        rgbMat=new Mat();        dstMat=new Mat();        Utils.bitmapToMat(mBitmap,rgbMat);    }    Imgproc.pyrUp(rgbMat,dstMat,new Size(mBitmap.getWidth(),mBitmap.getHeight()));    Utils.matToBitmap(dstMat,mBitmap);    mImage.setImageBitmap(mBitmap);}

这里在进行图像的缩放时需要注意的有两个地方, 在我的代码中在缩小之后将原图像设为缩小之后的图像了,这样是微课能够看到多次缩放之后的效果,第二点就是对于bitmap,在opencv教程代码的示例中是能够直接将Mat对象显示成图像的,不过在Android中并不能这样,所以需要将Mat对象转化为Bitmap对象,这样在缩小的过程中就会出现一个问题,Bitmap的大小如果和Mat的大小不一致的话就会报错,所以在我的代码中dstBitmap在初始化的时候是mBitmap的一半大小,这一点也需要注意。效果图:

可以看出来在多次点击缩小之后图像的清晰度明显下降。(我擦嘞,怎么把歌词也弄进去了?算了就这样吧,大家也可以猜猜是什么歌。)

简单的阈值操作

最简单的图像分割的方法。

应用举例:从一副图像中利用阈值分割出我们需要的物体部分(当然这里的物体可以是一部分或者整体)。这样的图像分割方法是基于图像中物体与背景之间的灰度差异,而且此分割属于像素级的分割。

为了从一副图像中提取出我们需要的部分,应该用图像中的每一个像素点的灰度值与选取的阈值进行比较,并作出相应的判断。(注意:阈值的选取依赖于具体的问题。即:物体在不同的图像中有可能会有不同的灰度值。

一旦找到了需要分割的物体的像素点,我们可以对这些像素点设定一些特定的值来表示。(例如:可以将该物体的像素点的灰度值设定为:‘0’(黑色),其他的像素点的灰度值为:‘255’(白色);当然像素点的灰度值可以任意,但最好设定的两种颜色对比度较强,方便观察结果)。

以上内容来自opencv教程。
用到的函数: Imgproc.threshold(rgbMat,dstMat,n,255,operation);其中参数:原图像,输出图像,阈值大小,设定的最大灰度值(该参数运用在二进制与反二进制阈值操作中),阈值方式。
说到阈值化,opencv教程上介绍了五种:
- 二进制阈值化 CV_THRESH_BINARY 0
- 反二进制阈值化 CV_THRESH_BINARY_INV 1
- 截断阈值化 CV_THRESH_TRUNC 2
- 阈值化为0 CV_THRESH_TOZERO 3
- 反阈值化为0 CV_THRESH_TOZERO_INV 4

好像怎么调都不齐,算了,一共这五种阈值化方式,中间的是android中的常量,后面是对应的int值。代码~~~~:

 private void changeBitmap() {    if(rgbMat==null|dstMat==null){    rgbMat=new Mat();    dstMat=new Mat();    }    Utils.bitmapToMat(grayBitmap,rgbMat);    Imgproc.threshold(rgbMat,dstMat,n,255,operation);    Utils.matToBitmap(dstMat,dstBitmap);    dstImage.setImageBitmap(dstBitmap);}

在这段代码之前先转化为灰度图之后在进行阈值化,看起来比较清晰,灰度化的代码就不写了,挺简单的,虽然阈值化也挺简单,就是调用一个函数而已,接下来就是效果图了。

仔细看的能够看出来,前两个是相反的,后两个也是相反的,有的时候回出现一些紫色和绿色不知道为什么,有专门学opencv的求指教。

实现自己的线性滤波器

本以为opencv会很好些,但写着写着就感觉好累啊,不过说出去的话就是泼出去的水,咱们继续吧。
滤波器之前看我博客的话应该有所了解,opencv中也为我们提供了好几种成熟的滤波器,现在我们可能因为业务需要,要自己写一个,用的方法就是filter2D。

套路好像不对,应该先上代码的。

Mat kernel=Imgproc.getStructuringElement(shape,new Size(2*n+1,2*n+1),new Point(n,n));    Imgproc.filter2D(rgbMat,dstMat,-1,kernel);

这回就简单写了,首先创建一个内核对象,之前看过的,之后就是调用filter2D()这个方法。参数简单介绍一下,前两个不说了,第三个是指图像深度,不是学图像的不是很懂,-1就是与原图像深度相同,第四个是内核,之后其他的重载方法中还有锚点位置,还有两个,最后一个应该是重复次数,剩下的那个就不是很理解,说是会在卷积过程中,该值会加到每个像素上。默认情况下,这个值为 0。更多的信息可以参考opencv教程。

给图像添加边界

本来还以为opencv4android中没有这个方法那,是在Sobel导数中发现的这个给图像添加边界的这个方法。看来还是得好好研究研究Core啊。代码:

 Utils.bitmapToMat(mBitmap,rgbMat);    Core.copyMakeBorder(rgbMat,dstMat,10,10,10,10,Core.BORDER_CONSTANT,new Scalar(255,0,0));    Log.d("TAG",dstMat.rows()+"/"+dstMat.cols()+"/"+mBitmap.getWidth()+"/"+mBitmap.getHeight());    Log.d("TAG","dst:"+dstBitmap.getWidth()+"/"+dstBitmap.getHeight());    Utils.matToBitmap(dstMat,dstBitmap);    dstImage.setImageBitmap(dstBitmap);

一个很简单的方法copyMakeBorder()八个参数,前六个分别是原图像, 目标图像,上下左右宽度,第七个是边界填充方式,有两种,另一种就在Core.BORDER_CONSTANT的下面,最后一个是样式,我选择红色的。本以为很简单的问题,结果一直在报错,后来进过查证才发现是因为在通过矩阵使边界变大的时候我们的Bitmap对象没有变大,所有通过打Log发现矩阵从原来的611变成631,而dstBitmap还是611的,所以一直报错,在创建的时候加20就好了!以后一定要注意这种在Mat转换的时候所对应的Bitmap大小是否合适,上一次在放大和缩小的时候就吃一次亏了,不长记性啊!

Sobel导数

Sobel 算子是一个离散微分算子 (discrete differentiation operator)。 它用来计算图像灰度函数的近似梯度。

Sobel 算子结合了高斯平滑和微分求导。

 if(rgbMat==null|dstMat==null){    rgbMat=new Mat();    dstMat=new Mat();    }    Mat gray=new Mat();//灰度图    Mat abs_grayx=new Mat();//灰度图X方向求导    Mat abs_grayy=new Mat();//灰度图Y方向求导    Utils.bitmapToMat(mBitmap,rgbMat);    Imgproc.GaussianBlur(rgbMat,rgbMat,new Size(3,3),0,0);    Imgproc.cvtColor(rgbMat,gray,Imgproc.COLOR_BGR2GRAY);    Imgproc.Sobel(gray,abs_grayx,-1,1,0,3);//X方向求导,-1为深度,1为求导次数    Imgproc.Sobel(gray,abs_grayy,-1,0,1,3);//Y方向求导,-1为深度,1为求导次数    Core.addWeighted(abs_grayx,0.5,abs_grayy,0.5,0,dstMat);//合并梯度    Utils.matToBitmap(dstMat,dstBitmap);    dstImage.setImageBitmap(dstBitmap);}

看一下效果。

opencv教程中效果。

基本就是一样的(废话,算法都是一样的),最开始的时候在调用Sobel函数的时候,以为在Imgproc这个了中没有找到addWeighted这个方法,就直接在Sobel中把XY方向的导数全设置为1了,跑也没问题,就是轮廓太细太少了,后来在Core核心中尝试了一下,发现了合并的方法,就按照opencv教程中的例子去写。效果可以,有Sobel对于检测物体轮廓应该有很大的帮助吧,到目前为止我都不知道我写的这些东西有什么用。

总结

opencv4android的内容已经写了两篇了,发现这么写太慢了,这才写了8个而且还都是Imgproc的,光Imgproc的就还有17个,而且还有其他的模块,我是一定要写的,虽然我不知道这个东西有什么用,但我知道是有用的,接下来的写的可能会简单粗糙一些,直接就是方法代码图片,尽量加快速度,还有那么多的东西要去学,那么多未知等待去探索,不能墨迹。接下来的Imgproc还有17个准备8个一篇9个一篇剩下的还有四五个大模块我还没看,等具体再定,估计整体下来没有20篇博客搞不定,加油吧!

0 0
原创粉丝点击