前语

  作业中遇到需求经过OpenCV找到图片主体体积占图片百分比的份额,这儿做一个问题解决思路的记载。该方面新手小白,有不对的当地能够谈论指出哈 。

重要API

Sobel算法

Sobel 核算参阅文章

索贝尔算子是核算机视觉领域的一种重要处理办法。 首要用于获得数字图画的一阶梯度,常见的应用和物理意义是边际检测。 索贝尔算子是把图画中每个像素的上下左右四领域的灰度值加权差,在边际处达到极值从而检测边际。在技术上,它是一离散性差分算子,用来运算图画亮度函数的梯度之近似值。在图画的任何一点运用此算子,将会发生对应的梯度矢量或是其法矢量。索贝尔算子不但发生较好的检测效果,并且对噪声具有滑润抑制作用,可是得到的边际较粗,且或许出现伪边际。 该算子包括两组3×3的矩阵,别离为横向及纵向,将之与图画作平面卷积,即可别离得出横向及纵向的亮度差分近似值。假如以A代表原始图画,Gx及Gy别离代表经横向及纵向边际检测的图画,其公式如下:

Android OpenCV(二)主体识别 位置检测
图画的每一个像素的横向及纵向梯度近似值可用以下的公式结合,来核算梯度的巨细。
Android OpenCV(二)主体识别 位置检测
然后可用以下公式核算梯度方向。
Android OpenCV(二)主体识别 位置检测
在以上比如中,假如以上的角度等于零,即代表图画该处拥有纵向边际,左方较右方暗 【引用:百度百科】

//src : 输入图画  dst: 输出图画
//ddepth: 输出图画的深度(能够理解为数据类型),-1表明与原图画相同的深度
//dx:  dy: 当组合为dx=1,dy=0时求x方向的一阶导数,当组合为dx=0,dy=1时求y方向的一阶导数
//ksize: (可选参数)Sobel算子的巨细,有必要是1,3,5或许7,默以为3。
//scale: 对导数核算成果进行缩放的缩放因子,默许系数为1,不进行缩放
//delta: 偏值,在核算成果中加上偏值。
//borderType: 像素外推法挑选标志。默许参数为BORDER_DEFAULT,表明不包括鸿沟值倒序填充。
public static void Sobel(Mat src, Mat dst, int ddepth, int dx, int dy, int ksize, double scale, double delta, int borderType) 
  • 留意点
  1. 图画深度是指存储每个像素值所用的位数,例如cv2.CV_8U,指的是8位无符号数,取值范围为0~255,超出范围则会被截断(截断指的是,当数值大于255保存为255,当数值小于0保存为0,其他不变)。   详细还有:CV_16S(16位无符号数),CV_16U(16位有符号数),CV_32F(32位浮点数),CV_64F(64位浮点数)   当ddepth 输出图画深度选用CV_8U,因为Sobel算子在核算X方向梯度时,假如某像素点右侧像素值大于左边像素值,则梯度巨细为正保存,相反梯度巨细为负被截断,梯度巨细保存为0。这儿能够运用 CV_32F 防止数据存在负数情况。

  2. 在咱们运用CV_32F数据类型求出XY阶后,需求合作运用函数convertScaleAbs()将图画深度为CV_64F的梯度图画重新转化为CV_8U,这是因为函数cv2.imshow()的默许显现为8位无符号数,即[0,255]。

详细思路

  第一步,运用OpenCV的 Sobel算子来核算x,y方向上的梯度,在x方向上减去y方向上的梯度,经过这个减法,咱们会留下有高水平梯度和低笔直梯度的图画区域。

        Mat gradX = new Mat();
        Mat gradY = new Mat();
        Mat binary = new Mat();
        //求X Y 阶 转换为32位浮点数个数
        Imgproc.Sobel(grayImage, gradX, CvType.CV_32F, 1, 0, -1);
        Imgproc.Sobel(grayImage, gradY, CvType.CV_32F, 0, 1, -1);
        //相减
        Core.subtract(gradX, gradY, binary);
        //转换数据格式CV_8U
        Core.convertScaleAbs(binary, binary);

以上步骤咱们能够得到一个带有许多噪点的鸿沟图,为了剔除掉那些噪点对于主体的判断,咱们需求运用 blur 办法 和是非两极化对图片进行处理

  第二步,运用低通滤泼器滑润图画(9 x 9内核),这将有助于滑润图画中的高频噪声。低通滤波器的目标是下降图画的变化率。如将每个像素替换为该像素周围像素的均值。这样就能够滑润并代替那些强度变化显着的区域。

//去噪
Imgproc.blur(binary, binary, new Size(9.0, 9.0));
//对含糊图画二值化。梯度图画中不大于90的任何像素都设置为0(黑色)。 不然,像素设置为255(白色)为第五步做准备作业
Imgproc.threshold(binary, binary, 90.0, 255.0, Imgproc.THRESH_BINARY | Imgproc.THRESH_OTSU);

  第三步,经过上述办法能够得到一个是非图,可是图片中或许会存在许多空白空隙, 咱们要用白色填充这些空余,使得后边的程序更容易辨认主体区域,这需求做一些形态学方面的操作。

//为形态学操作返回指定巨细和形状的结构元素 用于形态学处理
Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(25.0, 25.0));
// 运用腐蚀和膨胀作为根本操作来履行高级形态学转换
Imgproc.morphologyEx(binary,binary,Imgproc.MORPH_CLOSE,kernel);

  第四步, 经过上述处理,或许还会存在一些 大的噪点, 能够经过形态学堕落和膨胀进行消除

Point p = new Point(-1.0, -1.0);
Imgproc.erode(binary, binary, ker, p, 4);
Imgproc.dilate(binary, binary, ker, p, 4);

  第五步,经过 findContours()函数框选出主体的位置信息

List<MatOfPoint> contours = new ArrayList<>();
Mat hierarchy = new Mat();
// binary : 要检索的图片,有必要是为二值图,即是非的
//hierarchy : 成果返回值
Imgproc.findContours(binary,contours,hierarchy,
                Imgproc.RETR_EXTERNAL,Imgproc.CHAIN_APPROX_SIMPLE);

findContours 函数 最终两个参数,mode 和 method 单独拿出来列举下 不同的参数代表的含义 mode : Imgproc.RETR_EXTERNAL 表明只检测外概括 Imgproc.RETR_LIST 检测的概括不树立等级关系 Imgproc.RETR_CCOMP 树立两个等级的概括,上面的一层为外鸿沟,里边的一层为内孔的鸿沟信息。假如内孔内还有一个连通物体,这个物体的鸿沟也在顶层。 Imgproc.RETR_TREE 树立一个等级树结构的概括。

method : Imgproc.CHAIN_APPROX_NONE 存储所有的概括点,相邻的两个点的像素位置差不超过1,即max(abs(x1-x2),abs(y2-y1))==1 Imgproc.CHAIN_APPROX_SIMPLE 压缩水平方向,笔直方向,对角线方向的元素,只保存该方向的结尾坐标,例如一个矩形概括只需4个点来保存概括信息

  成果都存储在 contours 数组中,咱们能够运用他进行一些事务判断。   因为脱敏原因,这儿没有demo图片,该篇文章 首要参阅文章对我的协助非常大,小白这儿仅仅做一个开发记载,今天的这篇文章就到这儿。

参阅文章

辨认色彩方块并提取概括

OpenCV自动检测图画中的物体并裁剪

Sobel()核算图画梯度的细节讲解

OpenCV API官网地址