图像处理 提取轮廓_python图像形态学轮廓处理&人脸边缘轮廓提取 - CSDN
  • 图像轮廓提取

    千次阅读 2017-09-15 11:57:21
    图像边缘提取,常见的方式是先对图片进行灰度处理,然后再利用图像梯度算法提取出边框。我们先来看效果图   经过处理后的前后对比,可以看到,图形的轮廓已经大致提取了。现在来看实现的代码 using System; ...

    对图像边缘提取,常见的方式是先对图片进行灰度处理,然后再利用图像梯度算法提取出边框。我们先来看效果图

     

    经过处理后的前后对比,可以看到,图形的轮廓已经大致提取了。现在来看实现的代码

    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.IO;
    using System.Linq;
    using System.Text;
    
    namespace BitmapOutlineSimulate
    {
        public class BitmapOutline
        {
            #region 内部变量
            private Bitmap _bitmap = null;
            #endregion
    
            public BitmapOutline(Bitmap bitmap)
            {
                _bitmap = bitmap;
            }
    
            #region Calculate
            public Bitmap Calculate(GradientTypeEnum gradientType, int threshold)
            {
                Bitmap grayBitmap = new Bitmap(_bitmap.Width, _bitmap.Height);
                for (int i = 0; i < _bitmap.Width; i++)
                {
                    for (int j = 0; j < _bitmap.Height; j++)
                    {
                        //得到像素的原始色彩        
                        var oColor = _bitmap.GetPixel(i, j);
                        //得到该色彩的亮度
                        var brightness = oColor.GetBrightness();
                        //用该亮度计算灰度
                        var gRGB = (int)(brightness * 255);
                        //组成灰度色彩
                        var gColor = Color.FromArgb(gRGB, gRGB, gRGB);
                        //最后将该灰度色彩赋予该像素
                        grayBitmap.SetPixel(i, j, gColor);
                    }
                }
    
                var gradientTemplate = Gradient.GetGradientTemplate(gradientType);
    
    
                var destBitmap = EdgeDectect(grayBitmap, gradientTemplate, threshold);
                return destBitmap;
            }
    
            //template为模板,nThreshold是一个阈值,
            //用来将模板游历的结果(也就是梯度)进行划分。
            //大于阈值的和小于阈值的分别赋予两种颜色,白或黑来标志边界和背景
            private Bitmap EdgeDectect(Bitmap grayBitmap, int[,] template, int nThreshold)
            {
                var destBitmap = new Bitmap(grayBitmap.Width, grayBitmap.Height);
                //取出和模板等大的原图中的区域
                int[,] gRGB = new int[3, 3];
                //模板值结果,梯度
                int templateValue = 0;
                //遍历灰度图中每个像素            
                for (int i = 1; i < grayBitmap.Width - 1; i++)
                {
                    for (int j = 1; j < grayBitmap.Height - 1; j++)
                    {
                        //取得模板下区域的颜色,即灰度
                        gRGB[0, 0] = grayBitmap.GetPixel(i - 1, j - 1).R;
                        gRGB[0, 1] = grayBitmap.GetPixel(i - 1, j).R;
                        gRGB[0, 2] = grayBitmap.GetPixel(i - 1, j + 1).R;
                        gRGB[1, 0] = grayBitmap.GetPixel(i, j - 1).R;
                        gRGB[1, 1] = grayBitmap.GetPixel(i, j).R;
                        gRGB[1, 2] = grayBitmap.GetPixel(i, j + 1).R;
                        gRGB[2, 0] = grayBitmap.GetPixel(i + 1, j - 1).R;
                        gRGB[2, 1] = grayBitmap.GetPixel(i + 1, j).R;
                        gRGB[2, 2] = grayBitmap.GetPixel(i + 1, j + 1).R;
                        //按模板计算
                        for (int m = 0; m < 3; m++)
                        {
                            for (int n = 0; n < 3; n++)
                            {
                                templateValue += template[m, n] * gRGB[m, n];
                            }
                        }
                        //将梯度之按阈值分类,并赋予不同的颜色
                        if (templateValue > nThreshold)
                        {
                            destBitmap.SetPixel(i, j, Color.FromArgb(255, 255, 255)); //白
                        }
                        else
                        {
                            destBitmap.SetPixel(i, j, Color.FromArgb(0, 0, 0)); //黑
                        }
                        templateValue = 0;
                    }
                }
                return destBitmap;
            }
            #endregion
        }
    }
    
    在Calculate函数中,先对图片进行了灰度处理,其原理就是获取图片的亮度然后与RGB色运算得到。

    在灰度处理后,使用图像梯度和阈值对图像进行处理。选择不同的梯度和阈值会处理出不同的结果,上图是使用Lapacian梯度和阈值125计算的结果。下面梯度的代码

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace BitmapOutlineSimulate
    {
        /// <summary>
        /// 梯度
        /// </summary>
        public class Gradient
        {
            #region 常量
            public static int[,] Lapacian =
            {
                { 1,1,1},
                { 1,-8,1},
                { 1,1,1}
            };
    
            /// <summary>
            /// Sobel算子-水平
            /// </summary>
            public static int[,] Sobel_Horizontal =
            {
                 { -1,-2,-1},
                { 0,0,0},
                { 1,2,1}
            };
    
            /// <summary>
            /// Sobel算子-垂直
            /// </summary>
            public static int[,] Sobel_Vertical =
           {
                 { -1,0,1},
                { -2,0,2},
                { -1,0,1}
            };
    
            /// <summary>
            /// Sobel算子-对角线1
            /// </summary>
            public static int[,] Sobel_Diagonal1 =
            {
                 { 0,1,2},
                { -1,0,1},
                { -2,-1,0}
            };
    
            /// <summary>
            /// Sobel算子-对角线2
            /// </summary>
            public static int[,] Sobel_Diagonal2 =
            {
                { -2,-1,0},
                { -1,0,1},
                { 0,1,2}
            };
    
            /// <summary>
            /// Prewitt算子-水平
            /// </summary>
            public static int[,] Prewitt_Horizontal =
            {
                { -1,-1,-1},
                { 0,0,0},
                { 1,1,1}
            };
    
            /// <summary>
            /// Prewitt算子-垂直
            /// </summary>
            public static int[,] Prewitt_Vertical =
            {
                { -1,0,1},
                { -1,0,1},
                { -1,0,1}
            };
    
    
            /// <summary>
            /// Prewitt算子-对角线1
            /// </summary>
            public static int[,] Prewitt_Diagonal1 =
            {
                { 0,1,1},
                { -1,0,1},
                { -1,-1,0}
            };
    
            /// <summary>
            /// Prewitt算子-对角线2
            /// </summary>
            public static int[,] Prewitt_Diagonal2 =
            {
                { -1,-1,0},
                { -1,0,1},
                { 0,1,1}
            };
            #endregion
    
            public static int[,] GetGradientTemplate(GradientTypeEnum gradientType)
            {
                int[,] gradientTemplate = null;
                switch (gradientType)
                {
                    case GradientTypeEnum.Lapacian:
                        {
                            gradientTemplate = Lapacian;
                            break;
                        }
                    case GradientTypeEnum.Sobel_Horizontal:
                        {
                            gradientTemplate = Sobel_Horizontal;
                            break;
                        }
                    case GradientTypeEnum.Sobel_Vertical:
                        {
                            gradientTemplate = Sobel_Vertical;
                            break;
                        }
                    case GradientTypeEnum.Sobel_Diagonal1:
                        {
                            gradientTemplate = Sobel_Diagonal1;
                            break;
                        }
                    case GradientTypeEnum.Sobel_Diagonal2:
                        {
                            gradientTemplate = Sobel_Diagonal2;
                            break;
                        }
                    case GradientTypeEnum.Prewitt_Horizontal:
                        {
                            gradientTemplate = Prewitt_Horizontal;
                            break;
                        }
                    case GradientTypeEnum.Prewitt_Vertical:
                        {
                            gradientTemplate = Prewitt_Vertical;
                            break;
                        }
                    case GradientTypeEnum.Prewitt_Diagonal1:
                        {
                            gradientTemplate = Prewitt_Diagonal1;
                            break;
                        }
                    case GradientTypeEnum.Prewitt_Diagonal2:
                        {
                            gradientTemplate = Prewitt_Diagonal2;
                            break;
                        }
                    default:
                        break;
                }
                return gradientTemplate;
            }
        }
    }
    

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace BitmapOutlineSimulate
    {
        public enum GradientTypeEnum
        {
            Lapacian,
    
            /// <summary>
            /// Sobel算子-水平
            /// </summary>
            Sobel_Horizontal,
    
            /// <summary>
            /// Sobel算子-垂直
            /// </summary>
            Sobel_Vertical,
    
            /// <summary>
            /// Sobel算子-对角线1
            /// </summary>
            Sobel_Diagonal1,
    
            /// <summary>
            /// Sobel算子-对角线2
            /// </summary>
            Sobel_Diagonal2,
    
            /// <summary>
            /// Prewitt算子-水平
            /// </summary>
            Prewitt_Horizontal,
    
            /// <summary>
            /// Prewitt算子-垂直
            /// </summary>
            Prewitt_Vertical,
    
            /// <summary>
            /// Prewitt算子-对角线1
            /// </summary>
            Prewitt_Diagonal1,
    
            /// <summary>
            /// Prewitt算子-对角线2
            /// </summary>
            Prewitt_Diagonal2
        }
    }
    
    灰度算法参考文章http://blog.csdn.net/chinaxhb/article/details/4038050
    图像梯度参考文章http://blog.csdn.net/swj110119/article/details/51777422

    转载请注明出处。

    展开全文
  • 轮廓检测指的是将图像中物体的边界提取出来并在图中用封闭的...在opencv中使用cv2.findcontours()提取轮廓,cv2.drawcontours()绘制轮廓。低版本opencv中的findcontours函数返回三个值,图像、轮廓列表以及轮廓层...

    轮廓检测指的是将图像中物体的边界提取出来并在图中用封闭的曲线描绘出来。相比于边缘检测,轮廓检测所提取的曲线一定是封闭的,并且提取的均为目标的边界,边缘检测注重的是图像中灰度变化剧烈的区域,不一定是边界。在opencv中使用cv2.findcontours()提取轮廓,cv2.drawcontours()绘制轮廓。低版本opencv中的findcontours函数返回三个值,图像、轮廓列表以及轮廓层析结构,高版本的不再返回图像。参数分别为输入图像、需要的轮廓类型mode、轮廓的近似方法method。其中mode分为以下几种:
    CV_RETR_EXTERNAL 只检测出最外轮廓即c0。图2中第一个轮廓指向最外的序列,除此之外没有别的连接。
    CV_RETR_LIST 检测出所有的轮廓并将他们保存到表(list)中,图2中描绘了这个表,被找到的9条轮廓相互之间由h_prev和h_next连接。这里并没有表达出纵向的连接关系,没有使用v_prev和v_next.
    CV_RETR_COMP 检测出所有的轮廓并将他们组织成双层的结构,第一层是外部轮廓边界,第二层边界是孔的边界。从图2可以看到5个轮廓的边界,其中3个包含孔。最外层边界c0有两个孔,c0之间的所有孔相互间由h_prev和h_next指针连接。
    CV_RETR_TREE 检测出所有轮廓并且重新建立网状的轮廓结构。图2中,根节点是最外层的边界c0,c0之下是孔h00,在同一层中与另一个孔h01相连接。同理,每个孔都有子节点(相对于c000和c010),这些子节点和父节点被垂直连接起来。这个步骤一直持续到图像最内层的轮廓,这些轮廓会成为树叶节点
    Method分为以下几种:
    CV_CHAIN_CODE 用freeman链码输出轮廓,其他方法输出多边形(顶点的序列)。
    CV_CHAIN_APPROX_NONE将链码编码中的所有点转换为点。
    CV_CHAIN_APPROX_SIMPLE压缩水平,垂直或斜的部分,只保存最后一个点。
    CV_CHAIN_APPROX_TC89_L1,CV_CHAIN_QPPROX_TC89_KCOS使用Teh-Chin链逼近算法中的一个。
    CV_LINK_RUNS与上述的算法完全不同,连接所有的水平层次的轮廓。
    Drawcotours函数要输入的参数是图像、输入轮廓、要绘制轮廓的编号(若为腐蚀,则绘制所有的轮廓)、绘制的轮廓颜色、轮廓的粗细。

    展开全文
  • Opencv处理图像--轮廓提取

    千次阅读 2018-08-08 15:48:36
    Opencv处理图像--轮廓提取 使用cvfindContours 对图像进行轮廓检测。 #include&lt;iostream&gt; #include&lt;opencv2/core/core.hpp&gt; #include&lt;opencv2/highgui/highgui.hpp&gt; ...

    Opencv处理图像--轮廓提取

    使用cvfindContours 对图像进行轮廓检测。

    #include<iostream>
    #include<opencv2/core/core.hpp>
    #include<opencv2/highgui/highgui.hpp>
    int main()
    {
    	
    	IplImage* img = cvLoadImage("E:\\test.bmp",0); 
    	IplImage* imgColor = cvCreateImage(cvGetSize(img),8,3);
    	IplImage* contoursImage = cvCreateImage(cvSize(img->width,img->height),8,1);
    
    	cvNamedWindow("hui");
    	cvShowImage("hui",img);
    	cvThreshold(img,img,100,255,CV_THRESH_BINARY); //二值化图像以100为阀值
    	cvNamedWindow("2");
    	cvShowImage("2",img);
    	
    	CvSeq* contours = 0,* contoursTemp =0; 
    	cvZero(contoursImage);
    	
    	cvCvtColor(img,imgColor,CV_GRAY2BGR); //颜色转化函数
    
    	
    	//img :需要提前的图片需要二值
    	//storage:存放的空间
    	//contours:指向轮廓提取的第一个轮廓
    	//sizeof(CvContour):采用的是哪一种结构以及大小
    	//CV_RETR_LIST:轮廓存储方式
    	//CV_CHAIN_APPROX_NONE:采用坐标集表示轮廓
    	CvMemStorage* storage = cvCreateMemStorage(0);
    	int total = cvFindContours(img,storage,&contours,sizeof(CvContour),CV_RETR_LIST,CV_CHAIN_APPROX_NONE,cvPoint(0,0)); 
    	printf("%d",total); //输出总的轮廓数
    	contoursTemp = contours;
    
    	while(contoursTemp!=0){
    		for(int i = 0;i<contoursTemp->total;i++){
    			CvPoint *pt = (CvPoint*)cvGetSeqElem(contoursTemp,i);
    			cvSetReal2D(contoursImage,pt->y,pt->x,255.0);
    			cvSet2D(imgColor,pt->y,pt->x,cvScalar(0,0,255.0));
    		}
    		contoursTemp = contoursTemp->h_next;  
    	}
    	cvNamedWindow("img");
    	cvShowImage("img",imgColor);
    	cvNamedWindow("contoursImage");
    	cvShowImage("contoursImage",contoursImage);
    	cvWaitKey(0);
    	return 0;
    }

    展开全文
  • 图像处理-提取轮廓

    2020-07-29 14:20:50
    图像处理提取图片轮廓并换色。针对示例的几幅图片,编写MATLAB代码进行实现
  • 形态学轮廓提取&人脸边缘轮廓检测

    形态学轮廓提取

    1. 流程分析
      这里写图片描述
      原图像imgA:
      这里写图片描述
      噪声滤除图像imgB:
      这里写图片描述
      腐蚀处理图像imgC:
      这里写图片描述
      相减操作图像imgD:
      这里写图片描述
      二值化处理结果imgE:
      这里写图片描述

    2. 原理分析:
      2.1膨胀:求像素的局部最大值
      将图像(或图像的一部分区域,我们称之为A)与核(我们称之为B)进行如下卷积操作:
      这里写图片描述
      2.2腐蚀:求像素的局部最小值
      将图像(或图像的一部分区域,我们称之为A)与核(我们称之为B)进行如下卷积操作:
      这里写图片描述
      (膨胀腐蚀原理详细可参见博客:http://blog.sina.com.cn/s/blog_6f57a7150100ooin.html 非常感谢博主的分享。)
      2.3噪声滤除:
      开运算: A∘ B=(A⊝B)⊕B
      闭运算: A·B=(A⊕B)⊝B
      噪声滤除: {[(A⊝B)⊕B]⊕B}⊝B=(A∘ B)·B
      这里写图片描述
      在上图中,(a)是原图像,外部有噪声块,内部有噪声孔,(b)为结构元素,尺寸大于所有噪声块和噪声孔,(c)是用(b)去腐蚀(a)的结果,可见,外部的噪声块被去除;之后用(b)去膨胀(c)两次,得到(e),此时已经去除了内部的噪声孔,在进行一次腐蚀操作,得到和原图一样大小的去噪图像(f)。

    3. 实验对比:
      3.1图像格式对比:
      参数设置:不进行噪声滤除,腐蚀2次,十字核,尺寸3*3
      二值图像;
      这里写图片描述
      灰度图像:
      这里写图片描述
      RGB图像:
      这里写图片描述

      3.2核样式对比:
      参数设置:RGB图像,不进行”噪声滤除”,腐蚀1次,尺寸5*5,二值化阈值100.
      这里写图片描述

      3.3核尺寸对比:
      参数设置:RGB图像,不进行”噪声滤除”,十字型核样式,腐蚀1次,二值化阈值100
      这里写图片描述

      3.4腐蚀次数对比:
      参数设置:RGB图像,不进行”噪声滤除”,十字型核样式,尺寸5*5,二值化阈值100
      这里写图片描述

      3.5噪声滤除强度对比:
      参数设置: RGB图像,十字型,腐蚀1次,核样式,尺寸5*5,二值化阈值100
      这里写图片描述

      3.6对比分析:
      图像格式:RGB格式提取轮廓效果较好,灰度和二值图像信息相对有限。
      核样式:十字型效果最佳,矩形和圆形容易造成噪点。
      核尺寸:尺寸过小,轮廓提取不完整;尺寸过大,容易出现噪点。
      腐蚀次数:腐蚀次数过小,轮廓提取不完整;次数过多,容易出现噪点,且轮廓边缘强度过大。
      噪声滤除:影响把图像轮廓深度信息提取。(标红区域)

    4. 代码:

    morphology.h

    #pragma once
    #include "stdafx.h"
    #include "opencv2/highgui/highgui.hpp"
    #include <opencv2/opencv.hpp>
    #include <iostream>
    using namespace cv;
    using namespace std;
    
    Mat getKernelMatrix(int shape, Size ksize);//内核矩阵生成
    void erodeFun(Mat &src, Mat &dst, Mat kernel, int iterations);//腐蚀操作
    void dilateFun(Mat &src, Mat &dst, Mat kernel, int iterations);//膨胀操作
    void openFun(Mat &src, Mat &dst, Mat kernel, int iterations);//开操作
    void closeFun(Mat &src, Mat &dst, Mat kernel, int iterations);//闭操作
    void subtractFun(Mat src, Mat erode_ouput, Mat &externalGradientImg, int threshold);//减操作

    morphology.cpp

    #include "stdafx.h"
    #include "morphology.h"
    /*
    函数: 获取内核矩阵
    参数: shape内核样式(矩形: MORPH_RECT 交叉形十字: MORPH_CROSS 椭圆形: MORPH_ELLIPSE)
            ksize内核大小
    */
    Mat getKernelMatrix(int shape, Size ksize) {
        Point center((int)ksize.width / 2, (int)ksize.height / 2);
        int i, j;
        int r = 0, c = 0;//圆参数
        double helperR = 0;//圆参数
    
        if (ksize == Size(1, 1))//单位内核都是矩形内核
            shape = MORPH_RECT;
    
        if (shape == MORPH_ELLIPSE)//椭圆形内核参数处理
        {
            r = ksize.height / 2;
            c = ksize.width / 2;
            helperR = r ? 1. / ((double)r*r) : 0;
        }
    
        Mat kernel(ksize, CV_8U);
        //行遍历
        for (i = 0; i < ksize.height; i++)
        {
            uchar* ptr = kernel.data + i*kernel.step;
            int j1 = 0, j2 = 0;
    
            //确定每一行1的区间[j1,j2]
            if (shape == MORPH_RECT || (shape == MORPH_CROSS && i == center.y))
                j2 = ksize.width;
            else if (shape == MORPH_CROSS)
                j1 = center.x, j2 = j1 + 1;
            else
            {
                int dy = i - r;
                if (std::abs(dy) <= r)//圆
                {
                    int dx = saturate_cast<int>(c*sqrt((r*r - dy*dy)*helperR));
                    j1 = std::max(c - dx, 0);
                    j2 = std::min(c + dx + 1, ksize.width);
                }
            }
            //赋值操作
            for (j = 0; j < j1; j++)
                ptr[j] = 0;
            for (; j < j2; j++)
                ptr[j] = 1;
            for (; j < ksize.width; j++)
                ptr[j] = 0;
        }
    
        return kernel;
    }
    
    /*
    函数: 腐蚀操作
    参数: src:原图像
            dst:结果图像
            kernel:核矩阵
            iterations:操作次数
    */
    void erodeFun(Mat &src, Mat &dst, Mat kernel, int iterations) {
        //参数判定
        if (iterations == 0) { return; }
        if (kernel.cols == 1 && kernel.rows == 1){ return; }
    
    
        Mat_<uchar> kernelMatrix = kernel;
        int HALFKERLEN = kernelMatrix.cols / 2;//正方形
        int KERLEN = kernelMatrix.cols;
        Mat pre = src.clone();
        Mat next = src.clone();
    
        for (int iterator = 0; iterator < iterations; iterator++) {//腐蚀次数
            for (int i = 0; i<pre.rows; i++)
            {
                for (int j = 0; j < pre.cols; j++)
                {
                    //处理每一个像素值的每一个通道
                    Vec3b minPixel = pre.at<Vec3b>(i, j);
                    //遍历核
                    for (int ki = 0; ki < KERLEN; ki++) {
                        for (int kj = 0; kj < KERLEN; kj++) {
                            if ((int)kernelMatrix.at<uchar>(ki, kj) == 1) {//核为1,处理
                                int pi = ki + i - HALFKERLEN;
                                int pj = kj + j - HALFKERLEN;
                                if (pi >= 0 && pi < pre.rows && pj >= 0 && pj < pre.cols)//防止超限
                                {
                                    if (pre.at<Vec3b>(pi, pj)[0] < minPixel[0]) {
                                        minPixel[0] = pre.at<Vec3b>(pi, pj)[0];
                                    }
                                    if (pre.at<Vec3b>(pi, pj)[1] < minPixel[1]) {
                                        minPixel[1] = pre.at<Vec3b>(pi, pj)[1];
                                    }
                                    if (pre.at<Vec3b>(pi, pj)[2] < minPixel[2]) {
                                        minPixel[2] = pre.at<Vec3b>(pi, pj)[2];
                                    }
                                }
                            }
                        }
                    }
                    next.at<Vec3b>(i, j) = minPixel;
                }
            }
            pre = next.clone();//更新
        }
        dst = next.clone();//更新
    }
    
    
    /*
    函数: 膨胀操作
    参数: src:原图像
            dst:结果图像
            kernel:核矩阵
            iterations:操作次数
    */
    void dilateFun(Mat &src, Mat &dst, Mat kernel, int iterations) {
    
        Mat_<uchar> kernelMatrix = kernel;
        int HALFKERLEN = kernelMatrix.cols / 2;//正方形
        int KERLEN = kernelMatrix.cols;
        Mat pre = src.clone();
        Mat next = src.clone();
    
        for (int iterator = 0; iterator < iterations; iterator++) {//膨胀次数
            for (int i = 0; i<pre.rows; i++)
            {
                for (int j = 0; j < pre.cols; j++)
                {
                    //处理每一个像素值的每一个通道
                    Vec3b maxPixel = pre.at<Vec3b>(i, j);
                    //遍历核
                    for (int ki = 0; ki < KERLEN; ki++) {
                        for (int kj = 0; kj < KERLEN; kj++) {
                            if ((int)kernelMatrix.at<uchar>(ki, kj) == 1) {//核为1,处理
                                int pi = ki + i - HALFKERLEN;
                                int pj = kj + j - HALFKERLEN;
                                if (pi >= 0 && pi < pre.rows && pj >= 0 && pj < pre.cols)//不超限
                                {
                                    if (pre.at<Vec3b>(pi, pj)[0] > maxPixel[0]) {
                                        maxPixel[0] = pre.at<Vec3b>(pi, pj)[0];
                                    }
                                    if (pre.at<Vec3b>(pi, pj)[1] > maxPixel[1]) {
                                        maxPixel[1] = pre.at<Vec3b>(pi, pj)[1];
                                    }
                                    if (pre.at<Vec3b>(pi, pj)[2] > maxPixel[2]) {
                                        maxPixel[2] = pre.at<Vec3b>(pi, pj)[2];
                                    }
                                }
                            }
                        }
                    }
                    next.at<Vec3b>(i, j) = maxPixel;
                }
            }
            pre = next.clone();
        }
        dst = next.clone();
    }
    
    
    /*
    函数: 开操作
    参数: src:原图像
            dst:结果图像
            kernel:核矩阵
            iterations:操作次数
    */
    void openFun(Mat &src, Mat &dst, Mat kernel, int iterations) {
        Mat erode = src.clone();
        erodeFun(src, erode, kernel, iterations);   //腐蚀
        dilateFun(erode, dst, kernel, iterations);//膨胀
    }
    
    /*
    函数: 闭操作
    参数: src:原图像
            dst:结果图像
            kernel:核矩阵
            iterations:操作次数
    */
    void closeFun(Mat &src, Mat &dst, Mat kernel, int iterations) {
        if (iterations == 0) { return; }
        Mat dilate = src.clone();
        dilateFun(src, dilate, kernel, iterations); //腐蚀
        erodeFun(dilate, dst, kernel, iterations);//膨胀
    }
    
    
    void subtractFun(Mat src, Mat erode_ouput, Mat &externalGradientImg, int threshold) {
    
        for (int i = 0; i < src.rows; i++) {
            for (int j = 0; j < src.cols; j++) {
                /*cout << (int)src.at<Vec3b>(i, j)[0] << " ";
                cout << (int)src.at<Vec3b>(i, j)[1] << " ";
                cout << (int)src.at<Vec3b>(i, j)[2] << " " << endl;*/
                if (src.at<Vec3b>(i, j)[0] - erode_ouput.at<Vec3b>(i, j)[0] > threshold ||
                    src.at<Vec3b>(i, j)[1] - erode_ouput.at<Vec3b>(i, j)[1] > threshold ||
                    src.at<Vec3b>(i, j)[2] - erode_ouput.at<Vec3b>(i, j)[2] > threshold) {
                    externalGradientImg.at<Vec3b>(i, j)[0] = 255;
                    externalGradientImg.at<Vec3b>(i, j)[1] = 255;
                    externalGradientImg.at<Vec3b>(i, j)[2] = 255;
    
                }
                else {
                    externalGradientImg.at<Vec3b>(i, j)[0] = 0;
                    externalGradientImg.at<Vec3b>(i, j)[1] = 0;
                    externalGradientImg.at<Vec3b>(i, j)[2] = 0;
                }
    
            }
        }
    }

    main.cpp

    #include "stdafx.h"
    #include "opencv2/imgproc/imgproc.hpp"
    #include "opencv2/highgui/highgui.hpp"
    #include <stdlib.h>
    #include <stdio.h>
    #include "morphology.h"
    
    using namespace cv;
    /// 全局变量
    Mat src, outLineResult;
    
    int kernel_type = 0;//核样式
    int const kernel_type_max = 2;
    int kernel_size = 0;//核尺寸
    int const kernel_size_max= 9;
    int erode_times = 0;//腐蚀次数
    int const erode_times_max = 5;
    int removedetails_times = 0;//去除细节次数
    int const removedetails_times_max = 2;
    
    void outLine(int, void*);
    
    int main()
    {
        src = imread("test.png");
    
        if (!src.data)
        {
            return -1;
        }
    
        /// 创建显示窗口
        namedWindow("Erosion Demo", CV_WINDOW_AUTOSIZE);
        namedWindow("Dilation Demo", CV_WINDOW_AUTOSIZE);
        cvMoveWindow("Dilation Demo", src.cols, 0);
    
        createTrackbar("核样式:", "Erosion Demo",
            &kernel_type, kernel_type_max,
            outLine);
        createTrackbar("核尺寸:", "Erosion Demo",
            &kernel_size, kernel_size_max,
            outLine);
        createTrackbar("腐蚀次数:", "Erosion Demo",
            &erode_times, erode_times_max,
            outLine);
        createTrackbar("去噪强度:", "Erosion Demo",
            &removedetails_times, removedetails_times_max,
            outLine);
        /// Default start
        outLine(0, 0);
    
    
        waitKey(0);
        return 0;
    }
    
    /**  @function Erosion  */
    void outLine(int, void*)
    {
        int type;
        string tpyeStr = "";
        string removeDetailType = "";
        if (kernel_type == 0) { type = MORPH_RECT; tpyeStr = "正方形"; }
        else if (kernel_type == 1) { type = MORPH_CROSS; tpyeStr = "十字形"; }
        else if (kernel_type == 2) { type = MORPH_ELLIPSE; tpyeStr = "圆形"; }
    
        //输出
        cout << endl<<"----------------------" << endl;
        cout << "  核样式:"<< tpyeStr;
        cout << "  核尺寸:" << kernel_size;
        cout << "  轮廓强度:" << erode_times;
        cout << "  细节去除样式:" << removeDetailType;
        cout << "  细节去除强度:" << removedetails_times;
    
        Mat element = getKernelMatrix(type, Size(kernel_size, kernel_size));
        Mat erode_ouput,closeResult,openResult;
        erode_ouput = src.clone();
        outLineResult = src.clone();
        openResult = src.clone();
        closeResult = src.clone();
        //噪声滤除
        if (removedetails_times == 1) {
            closeFun(src, closeResult, element, 1);
            openFun(closeResult, openResult, element, 1);
        }
        else if (removedetails_times == 2) {
            openFun(src, openResult, element, 1);//去噪
            closeFun(openResult, closeResult, element, 1);
            openFun(closeResult, openResult, element, 1);//去噪
            closeFun(openResult, closeResult, element, 1);  
        }
    
        // 腐蚀操作
        erodeFun(closeResult, erode_ouput, element , erode_times);
    
        //减操作
        subtractFun(closeResult, erode_ouput, outLineResult,50);
    
        imshow("Erosion Demo", outLineResult);
        cout << "  完成";
    
    }
    

    人脸边缘轮廓提取(下巴、嘴唇)

    使用上述方法对人脸进行轮廓提取,调整参数,得到相对最佳的效果,但是会出现下巴和嘴唇提取失败的现象,所以针对这些边缘信息,要自行提取。

    1. 流程分析:
      这里写图片描述

    2. 嘴唇定位&内外轮廓及中心点确认:

      使用人脸特征提取和模式匹配方法去定位嘴唇并确认中心点以及内外轮廓。
      这里写图片描述

    3. 自适应算法确定轮廓点
      这里写图片描述

    Step1:确定路径L(i)的起始点start(i)和终止点end(i) 。
    Step2:从start(i)向end(i)做步长为1的像素点搜索,每次搜索计算像素梯度值,当梯度值大于设定的阈值时,停止该次搜索,并保存该点为轮廓点。
    Step3:确定是否完成搜索:如完成,进行下一步轮廓提取。如果未完成,更新搜索路径L(i+1),转至Step1.

    提取结果:
    这里写图片描述

    改变搜索方向,即可对嘴唇进行轮廓点的提取:
    这里写图片描述

    展开全文
  • 实验三 图像轮廓提取与边缘检测 一、实验目的: 理解并掌握对二值图像进行轮廓提取的相关算法(比如,掏空内部点法),以及用于图像边缘检测和提取的典型微分算子(梯度算子和拉普拉斯算子)。 二、实验环境...
  • 图像预处理-寻找图像轮廓的骨架 由于图片中的对象之间互相粘连和遮挡,因此必须前景进行细化,尽量使不同目标之间分开,更有利于后续的骨架提取。Opencv自带寻找轮廓的函数:流程是:获取灰度图→图片二值化→寻找...
  • 快乐虾...测试图像仍然是它:首先要做的当然是对图像进行分区域处理。在上一步中我们得到了标识绿色植物的二值图像,一个很自然的想法是利用此二值图像轮廓进行分块。[python] view plain copy ...
  • 本文内容参考《数字图像处理基础》Wilhelm Burger等著。 根据图像数组获得边缘检测信息,然后循着已检测到的边缘点找到轮廓线。轮廓跟踪:从那些边缘强度较大的地方开始,沿着两个不同方向跟踪边缘点,直到这两条...
  • 8位活24位图像的基于sobel轮廓提取,区域生长算法分割图像; 灰度化,二值分割等
  • VC图像轮廓提取经典算法

    热门讨论 2020-07-30 23:30:53
    对经典的roberts,sobel,拉普拉斯,轮廓跟踪等算法进行了VC编程。程序给出了很好的源码,并且能运行。
  • 目标物体的轮廓提取 轮廓提取法 边界跟踪法 区域增长法 区域分裂合并法 一、轮廓提取法 对于二值图像轮廓提取,我们可以采用掏空内部点法,如果原图中有一点为黑,且它的8个相邻的点都为黑,则将该点删除。对于...
  • 图像轮廓提取知识总结

    千次阅读 2017-04-13 11:12:26
    图像轮廓提取 1 基于区域的方法 分割+提取 基于灰度、颜色、纹理等来进行分割, 分割方法:区域生长(计算简单、均匀区域效果好,但是人为确定种子点、容易空洞、噪声敏感)  分水岭变换(容易过分割) ...
  • 使基于图像边缘提取的基础寻找对象轮廓的方法,所以便于提取的阈值选定会影响最终轮廓发现的结果 API介绍: findContours 发现轮廓 drawContours 绘制轮廓 from matplotlib import pyplot as plt from cv2 import ...
  • OpenCV提取图像轮廓总结

    千次阅读 2014-06-06 14:42:33
    OpenCV提取图像轮廓总结
  •  神经网提取轮廓从我做过的角度来说的话,我认为可以分为三个部分,第一个部分是对人体的分割提取,第二使用边缘提取算子例如canny算子对人体轮廓进行粗略计算,第三部分,使用形态学及相关算法对轮廓图像进行修改...
  • #轮廓提取的原理做为一个初学者,试想如果提取轮廓?我首先想到的是沿着轮廓边界搜索。很显然,每个点有8个相邻点也就是8个方向,只要对每个点的8个方向做递归判断就可以了吧。这是我初次接触轮廓提取时的第一想法。...
  • OpenCV与EmguCV中的图像轮廓提取

    万次阅读 2016-07-19 12:55:22
    轮廓是图像中表示边界的一系列...openCV中可以用findContours()函数来从二值图像提取轮廓。openCV中一般用序列来存储轮廓信息。序列中的每一个元素是曲线中一个点的位置。函数findContours()从二值图像中寻找轮廓。f
  • 几种方法用来提取图像轮廓的例子

    万次阅读 2017-05-15 09:15:56
    参考了许多博客,自己整理了一下几种方法用来提取图像轮廓的方法,记录一下
  • 6、Halcon图像边缘提取轮廓识别

    千次阅读 2020-03-05 14:49:04
    1、图像边缘提取原理 2、边缘提取算子介绍 3、图像的亚像素边缘提取 4、亚像素轮廓的特征分析 5、xld的分割及直线拟合 6、圆及椭圆的拟合 7、中心线的提取 1、图像边缘提取原理 2、边缘提取算子介绍 ...
1 2 3 4 5 ... 20
收藏数 15,923
精华内容 6,369
关键字:

图像处理 提取轮廓