轮廓提取_轮廓提取算法 - CSDN
精华内容
参与话题
  • 提取轮廓的原理和代码实例

    千次阅读 2017-10-20 17:42:37
    在检测物体的轮廓时,我们...那么findcontour是基于什么原理来实现轮廓提取呢?1985年,有个叫satoshi suzuki的人发表了一篇论文,Topological structural analysis of digitized binary images by border followin

    在检测物体的轮廓时,我们通常会使用到opencv中的findcontour和drawcontour,比较常用而且效果不错。那么findcontour是基于什么原理来实现轮廓的提取呢?

    1985年,有个叫satoshi suzuki的人发表了一篇论文,Topological structural analysis of digitized binary images by border following,他介绍了两种算法来实现轮廓的提取,当然输入的图像是二值图像。findcontour就是基于这篇论文的思路来实现。

    本博文分为三部分来写,第一部分是介绍satoshi suzuki论文的大致思想;第二部分是使用实例来阐述findcontour的运用;第三部分是代码的解释。

    一、论文大概思想

    这篇论文我看了一天多,内容还是比较好理解。他主要介绍了两种算法,用来对数字二值图像进行拓扑分析。第一种算法是在确定二值图像边界的围绕关系,即确定外边界、孔边界以及他们的层次关系,由于这些边界和原图的区域具有一一对应关系(外边界对应像素值为1的连通区域,孔边界对应像素值为0的区域),因此我们就可以用边界来表示原图。第二种算法,是第一种算法的修改版本,本质一样,但是它只找最外面的边界。

    也许你会问,这个算法怎么来确定外边界,孔边界以及他们的层级关系?他采用编码的思想,给不同的边界赋予不同的整数值,从而我们就可以确定它是什么边界以及层次关系。输入的二值图像即为0和1的图像,用f(i,j)表示图像的像素值。每次行扫描,遇到以下两种情况终止:

    (1)f(i,j-1)=0,f(i,j)=1;//f(i,j)是外边界的起始点

    (2)f(i,j)>=1,f(i,j+1)=0;//f(i,j)是孔边界的起始点

    然后从起始点开始,标记边界上的像素。在这里分配一个唯一的标示符给新发现的边界,叫做NBD。初始时NBD=1,每次发现一个新边界加1。在这个过程中,遇到f(p,q)=1,f(p,q+1)=0时,将f(p,q)置为-NBD。什么意思呢?就是右边边界的终止点。假如一个外边界里有孔边界时,怎么推导呢?限于篇幅,你可以看论文的附录1。

    二、实例运用

    下面举个例子,对一幅图像先进行边沿提取,这里使用canny,然后再用findcontour提取轮廓,最后用drawcontour画出轮廓。

    #include "StdAfx.h"
    #include "opencv2/highgui/highgui.hpp"
    #include "opencv2/imgproc/imgproc.hpp"
    #include <iostream>
    #include <stdio.h>
    #include <stdlib.h>
    
    using namespace cv;
    using namespace std;
    
    Mat src; Mat src_gray;
    int thresh = 100;
    int max_thresh = 255;
    RNG rng(12345);
    
    /// Function header
    void thresh_callback(int, void* );
    
    /** @function main */
    int main()
    {
        /// Load source image and convert it to gray
        src = imread("lena.jpg", 1 );
    
        /// Convert image to gray and blur it
        cvtColor( src, src_gray, CV_BGR2GRAY );
        blur( src_gray, src_gray, Size(3,3) );
    
        /// Create Window
        char* source_window = "Source";
        namedWindow( source_window, 0);
        imshow( source_window, src );
    
        createTrackbar( " Canny thresh:", "Source", &thresh, max_thresh, thresh_callback );
        thresh_callback( 0, 0 );
    
        waitKey(0);
        return(0);
    }
    
    /** @function thresh_callback */
    void thresh_callback(int, void* )
    {
        Mat canny_output;
        vector<vector<Point> > contours;
        vector<Vec4i> hierarchy;
    
        /// Detect edges using canny
        Canny( src_gray, canny_output, thresh, thresh*2, 3 );
        /// Find contours
        findContours( canny_output, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );
    
        /// Draw contours
        Mat drawing = Mat::zeros( canny_output.size(), CV_8UC3 );
        for( int i = 0; i< contours.size(); i++ )
        {
            Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
            drawContours( drawing, contours, i, color, CV_FILLED, 8, hierarchy, 0, Point() );
        }
    
        /// Show in a window
        namedWindow( "Contours", 0 );
        imshow( "Contours", drawing );
    }

    结果图如下:

    这里写图片描述
    这里写图片描述

    三、代码注释

    findContours( canny_output, contours, hierarchy, 
    CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );

    canny_output:使用canny算子提取边缘的结果图,这里的图像必须是二值图像;
    contours:提取得到的轮廓图,每个轮廓作为一个点向量来存储;

    hierarchy:关于输出图像的拓扑信息,例如第i个轮廓,hierarchy[i][0]和hiearchy[i][1]表示该轮廓的前一个和后一个轮廓,同一层关系;hiearchy[i][2]和hiearchy[i][3]表示父轮廓和子轮廓,不同层的关系。

    CV_RETR_EXTERNAL:轮廓检索模式,这里采用外轮廓的模式;

    CV_CHAIN_APPROX_SIMPLE:轮廓的近似方法,这里采用压缩方式,只用端点来表示;

    Point(0,0):偏移量,这里无偏移。

    drawContours( drawing, contours, i, color,
    CV_FILLED, 8, hierarchy, 0, Point() );

    drawing:目标图像;
    contours:所有输入的轮廓;

    i:指定那个轮廓被画;

    color:轮廓的颜色

    CV_FILLED:线的宽度,这里采用填充模式;

    8:线的连接度;

    hierarchy:只有当你只需要画一些轮廓时,这个参数才需要:

    0:所画轮廓的最大级别,为0时表示只画指定的轮廓;

    Point():偏移量。

    哈哈,终于好了!

    展开全文
  • 图像轮廓提取知识总结

    千次阅读 2017-04-13 11:12:26
    图像轮廓提取 1 基于区域的方法 分割+提取 基于灰度、颜色、纹理等来进行分割, 分割方法:区域生长(计算简单、均匀区域效果好,但是人为确定种子点、容易空洞、噪声敏感)  分水岭变换(容易过分割) ...

    图像轮廓提取


    1 基于区域的方法

    分割+提取

    基于灰度、颜色、纹理等来进行分割,

    分割方法:区域生长(计算简单、均匀区域效果好,但是人为确定种子点、容易空洞、噪声敏感)

                      分水岭变换(容易过分割)

                      分裂合并(复杂、计算量大,分裂容易破坏边界)

                       图论


    2 基于边缘的方法

    分为:基于边缘检测和基于边缘分组

    基于边缘检测:边缘检测算子+去除杂点、冗余边缘、修复边缘(噪声敏感)(基于梯度的方法可以归纳到这里)

    基于边缘分组:线逼近算法去除噪声+边缘点逐步组合合并成(噪声敏感)


    3 基于活动轮廓的方法

    方法:snake模型法和水平集法

    原理:都是设定一个初始轮廓,不断迭代,直到内部外部能量函数和最小

    缺点:初始轮廓敏感


    4 基于视觉特性的方法

    原理:定义函数模拟人类的视觉特性,构建仿生模型进行特征提取


    展开全文
  • OpenCV学习指南(五)轮廓提取

    千次阅读 2020-04-11 13:23:44
    #图像中的轮廓提取 #时间:2019/1/3 #作者:cclplus #仅供学习交流使用 #如若照片本人认为侵犯了肖像权,请联系我的邮箱707101557@qq.com #如有疑问或者需求,可以联系作者的邮箱 #如果你有什么好的建议或者...

    前言

      有很多算法被用来实现提取轮廓,比如:Canny,阈值分割,提取傅里叶变换的高频信息,还有别具一格的蚁群算法,当然比较常见的作法是使用阈值分割+边缘查找,在OpenCV里是threshold和findContours两个函数的组合使用,和Canny。
      轮廓提取的算法很多,而其目的都是为了找到图像中灰阶差比较大的位置。而所谓亚像素提取,则是使用了插值算法,以找出灰阶差最大的位置。

    提取傅里叶变换的高频信息

    ##############
    #图像中的轮廓提取
    #时间:2019/1/3
    #作者:cclplus
    #仅供学习交流使用
    #如有疑问或者需求,可以联系作者的邮箱
    #如果你有什么好的建议或者指导,我将不胜感激
    
    
    import cv2
    import numpy as np
    from matplotlib import pyplot as plt
    import copy
    img = cv2.imread('liuyifei.jpg',0)
    f = np.fft.fft2(img)
    fshift = np.fft.fftshift(f)
    
    rows,cols = img.shape
    crow,ccol = int(rows/2) , int(cols/2)
    for i in range(crow-30,crow+30):
        for j in range(ccol-30,ccol+30):
            fshift[i][j]=0.0
    f_ishift = np.fft.ifftshift(fshift)
    img_back = np.fft.ifft2(f_ishift)#进行高通滤波
    # 取绝对值
    img_back = np.abs(img_back)
    plt.subplot(121),plt.imshow(img,cmap = 'gray')#因图像格式问题,暂已灰度输出
    plt.title('Input Image'), plt.xticks([]), plt.yticks([])
    #先对灰度图像进行伽马变换,以提升暗部细节
    rows,cols = img_back.shape
    gamma=copy.deepcopy(img_back)
    rows=img.shape[0]
    cols=img.shape[1]
    for i in range(rows):
        for j in range(cols):
            gamma[i][j]=5.0*pow(gamma[i][j],0.34)#0.34这个参数是我手动调出来的,根据不同的图片,可以选择不同的数值
    #对灰度图像进行反转
    
    for i in range(rows):
        for j in range(cols):
            gamma[i][j]=255-gamma[i][j]
    plt.subplot(122),plt.imshow(gamma,cmap = 'gray')
    plt.title('Result in HPF'), plt.xticks([]), plt.yticks([])
    plt.show()
    
    

    原图
    在这里插入图片描述
    输出结果
    在这里插入图片描述

    通过蚁群算法进行图片轮廓提取

    相关代码我上传到了我的github上
    https://github.com/YuruTu/Ant_colony

    在这里插入图片描述
      效果不够理想,这也算得上蚁群算法的一大特点,对参数要求较高,需要调参。相关内容,笔者会持续更新

    Canny边缘检测

      

    import cv2
    import numpy as np
    from matplotlib import pyplot as plt
    
    img = cv2.imread('liuyifei.jpg',0)
    edges = cv2.Canny(img,100,200)
    
    plt.subplot(121),plt.imshow(img,cmap='gray')
    plt.title('original'),plt.xticks([]),plt.yticks([])
    plt.subplot(122),plt.imshow(edges,cmap='gray')
    plt.title('edge'),plt.xticks([]),plt.yticks([])
    
    plt.show()
    

    在这里插入图片描述

    使用cuda加速提取轮廓

    #include <iostream>
    #include <cuda.h>
    #include <cstdlib>
    #include <stdio.h>
    #include <cuda_runtime.h>
    #include <string>
    #include <assert.h>
    #include <cuda_runtime.h>
    #include <cuda_runtime_api.h>
    #include <device_launch_parameters.h>
    #include <opencv2/core/core.hpp>
    #include <opencv2/imgproc/imgproc.hpp>
    #include <opencv2/highgui/highgui.hpp>
    
    using namespace cv;
    using namespace std;
    // GPU constant memory to hold our kernels (extremely fast access time)
    __constant__ float convolutionKernelStore[256];
    
    /**
     * Convolution funcion para cuda.  Destino tendra el mismo width/height como la fuente,
     *
     * @param source      Source imagen host
     * @param width       Source imagen width
     * @param height      Source imagen height
     * @param paddingX    source imagen padding x
     * @param paddingY    source imagen padding y
     * @param kOffset     offset en kernel almacenamiento de memoria constante
     * @param kWidth      kernel width
     * @param kHeight     kernel height
     * @param destination Imagen de destino
     */
    __global__ void convolve(unsigned char *source, int width, int height, int paddingX, int paddingY, unsigned int kOffset, int kWidth, int kHeight, unsigned char *destination)
    {
    	// Calculate our pixel's location
    	int x = (blockIdx.x * blockDim.x) + threadIdx.x;
    	int y = (blockIdx.y * blockDim.y) + threadIdx.y;
    
    	float sum = 0.0;
    	int   pWidth = kWidth / 2;
    	int   pHeight = kHeight / 2;
    
    	//Solo ejecuta validos pixeles
    	if (x >= pWidth + paddingX && y >= pHeight + paddingY && x < (blockDim.x * gridDim.x) - pWidth - paddingX &&
    		y < (blockDim.y * gridDim.y) - pHeight - paddingY)
    	{
    		for (int j = -pHeight; j <= pHeight; j++)
    		{
    			for (int i = -pWidth; i <= pWidth; i++)
    			{
    				// Sample the weight for this location
    				int ki = (i + pWidth);
    				int kj = (j + pHeight);
    				float w = convolutionKernelStore[(kj * kWidth) + ki + kOffset];
    
    
    				sum += w * float(source[((y + j) * width) + (x + i)]);
    			}
    		}
    	}
    
    	// Promedio sum
    	destination[(y * width) + x] = (unsigned char)sum;
    }
    
    __global__ void pythagoras(unsigned char *a, unsigned char *b, unsigned char *c)
    {
    	int idx = (blockIdx.x * blockDim.x) + threadIdx.x;
    
    	float af = float(a[idx]);
    	float bf = float(b[idx]);
    
    	c[idx] = (unsigned char)sqrtf(af*af + bf * bf);
    }
    
    // crea imagen buffer
    unsigned char* createImageBuffer(unsigned int bytes, unsigned char **devicePtr)
    {
    	unsigned char *ptr = NULL;
    	cudaSetDeviceFlags(cudaDeviceMapHost);
    	cudaHostAlloc(&ptr, bytes, cudaHostAllocMapped);
    	cudaHostGetDevicePointer(devicePtr, ptr, 0);
    	return ptr;
    }
    
    
    int main(int argc, char** argv) {
    	// Abre la camaraweb
    	cv::VideoCapture camera(0);
    	cv::Mat          frame;
    	if (!camera.isOpened())
    		return -1;
    
    	// capture windows
    	cv::namedWindow("Source");
    	cv::namedWindow("Greyscale");
    	cv::namedWindow("Blurred");
    	cv::namedWindow("Sobel");
    
    	// Funciones para obtener el tiempo de ejecucion 
    	cudaEvent_t start, stop;
    	cudaEventCreate(&start);
    	cudaEventCreate(&stop);
    
    	// Crea kernel gaussian(sum = 159)
    	const float gaussianKernel5x5[25] =
    	{
    		2.f / 159.f,  4.f / 159.f,  5.f / 159.f,  4.f / 159.f, 2.f / 159.f,
    		4.f / 159.f,  9.f / 159.f, 12.f / 159.f,  9.f / 159.f, 4.f / 159.f,
    		5.f / 159.f, 12.f / 159.f, 15.f / 159.f, 12.f / 159.f, 5.f / 159.f,
    		4.f / 159.f,  9.f / 159.f, 12.f / 159.f,  9.f / 159.f, 4.f / 159.f,
    		2.f / 159.f,  4.f / 159.f,  5.f / 159.f,  4.f / 159.f, 2.f / 159.f,
    	};
    	cudaMemcpyToSymbol(convolutionKernelStore, gaussianKernel5x5, sizeof(gaussianKernel5x5), 0);
    	const unsigned int gaussianKernel5x5Offset = 0;
    
    	// Sobel gradient kernels
    	const float sobelGradientX[9] =
    	{
    		-1.f, 0.f, 1.f,
    		-2.f, 0.f, 2.f,
    		-1.f, 0.f, 1.f,
    	};
    	const float sobelGradientY[9] =
    	{
    		1.f, 2.f, 1.f,
    		0.f, 0.f, 0.f,
    		-1.f, -2.f, -1.f,
    	};
    	cudaMemcpyToSymbol(convolutionKernelStore, sobelGradientX, sizeof(sobelGradientX), sizeof(gaussianKernel5x5));
    	cudaMemcpyToSymbol(convolutionKernelStore, sobelGradientY, sizeof(sobelGradientY), sizeof(gaussianKernel5x5) + sizeof(sobelGradientX));
    	const unsigned int sobelGradientXOffset = sizeof(gaussianKernel5x5) / sizeof(float);
    	const unsigned int sobelGradientYOffset = sizeof(sobelGradientX) / sizeof(float) + sobelGradientXOffset;
    
    	// Crea CPU/GPU imagenes compartidos
    	camera >> frame;
    	unsigned char *sourceDataDevice, *blurredDataDevice, *edgesDataDevice;
    	cv::Mat source(frame.size(), CV_8U, createImageBuffer(frame.size().width * frame.size().height, &sourceDataDevice));
    	cv::Mat blurred(frame.size(), CV_8U, createImageBuffer(frame.size().width * frame.size().height, &blurredDataDevice));
    	cv::Mat edges(frame.size(), CV_8U, createImageBuffer(frame.size().width * frame.size().height, &edgesDataDevice));
    
    	// Crea 2 imagenes temporales (sobel gradients)
    	unsigned char *deviceGradientX, *deviceGradientY;
    	cudaMalloc(&deviceGradientX, frame.size().width * frame.size().height);
    	cudaMalloc(&deviceGradientY, frame.size().width * frame.size().height);
    
    	// Loop while captura imagenes
    	while (1)
    	{
    		// Captura la imagen en escala de grises
    		camera >> frame;
    		cvtColor(frame, source, COLOR_RGB2GRAY);
    		_sleep(1);
    		// Graba el tiempo que demora el proceso
    		cudaEventRecord(start);
    		{
    			// convolution kernel  parametros
    			dim3 cblocks(frame.size().width / 16, frame.size().height / 16);
    			dim3 cthreads(16, 16);
    
    			// pythagoran kernel parametros
    			dim3 pblocks(frame.size().width * frame.size().height / 256);
    			dim3 pthreads(256, 1);
    
    			//  gaussian blur (first kernel in store @ 0)
    			convolve <<<cblocks, cthreads >> > (sourceDataDevice, frame.size().width, frame.size().height, 0, 0, gaussianKernel5x5Offset, 5, 5, blurredDataDevice);
    
    			// sobel gradient convolutions (x&y padding is now 2 because there is a border of 2 around a 5x5 gaussian filtered image)
    			convolve << <cblocks, cthreads >> > (blurredDataDevice, frame.size().width, frame.size().height, 2, 2, sobelGradientXOffset, 3, 3, deviceGradientX);
    			convolve << <cblocks, cthreads >> > (blurredDataDevice, frame.size().width, frame.size().height, 2, 2, sobelGradientYOffset, 3, 3, deviceGradientY);
    			pythagoras << <pblocks, pthreads >> > (deviceGradientX, deviceGradientY, edgesDataDevice);
    
    			cudaThreadSynchronize();
    		}
    		cudaEventRecord(stop);
    
    		// Muestra tiempo de ejecucion
    		float ms = 0.0f;
    		cudaEventSynchronize(stop);
    		cudaEventElapsedTime(&ms, start, stop);
    		std::cout << "Elapsed GPU time: " << ms << " milliseconds" << std::endl;
    
    		// Muestra resultados
    		imshow("Source", frame);
    		imshow("Greyscale", source);
    		imshow("Blurred", blurred);
    		imshow("Sobel", edges);
    
    		// Spin
    		if (cv::waitKey(1) == 27) break;
    	}
    
    	// Exit
    	cudaFreeHost(source.data);
    	cudaFreeHost(blurred.data);
    	cudaFreeHost(edges.data);
    	cudaFree(deviceGradientX);
    	cudaFree(deviceGradientY);
    
    	return 0;
    }
    

      很多时候加上Cuda是有必要的,如果你要使用hough变换之类的时间复杂度比较高的代码,Gpu编程会给你带来多个数量级的加速。
    以下是一段广告,诚信可靠:欢迎购买该课程入门python,特惠期只要CN¥:99
    https://study.163.com/course/courseMain.htm?courseId=1005699008&share=2&shareId=400000000537035

    展开全文
  • opencv 轮廓提取

    千次阅读 2017-08-30 15:27:54
    轮廓指的是将连续的点连接在一起的曲线,具有相同的灰度值或颜色,提取轮廓就是提取这些具有相同颜色或灰度值的曲线 寻找轮廓应该用二值图,这样更明显 findcontours会改变原图,应该用copyto保存原图 查找轮廓应该...

    轮廓指的是将连续的点连接在一起的曲线,具有相同的灰度值或颜色,提取轮廓就是提取这些具有相同颜色或灰度值的曲线

    寻找轮廓应该用二值图,这样更明显

    findcontours会改变原图,应该用copyto保存原图

    查找轮廓应该是在黑色背景中找到白色物体



    一:轮廓查找函数findContours( InputOutputArray image, OutputArrayOfArrays contours,int mode, int method, Point offset=Point());

    第一个参数是输入的二值图

    第二个参数是检测到的轮廓(vector<<vector>Point>contours),第一个<>里面的vector是一个轮廓所有点的向量数组,外面的vector是检测到的所有轮廓向量(以单个轮廓作为向量元素)

    第三个参数是 轮廓检索模式

    CV_RETR_EXTERNAL=0-----表示只检测最外层轮廓--常用
    CV_RETR_LIST=1------提取所有轮廓并放置在list中, 轮廓不建立等级关系
    CV_RETR_CCOMP=2------提取所有轮廓并组织为双层结构
    CV_RETR_TREE=3------提取所有轮廓并重新建立网状轮廓结构


    第四个参数是 轮廓近似方法

    CHAIN_APPROX_NONE--常用--提取连续的轮廓线

     CHAIN_APPROX_SIMPLE--只显示线段端点


    第五个参数是偏移量,可以不设置


    二:轮廓绘制函数---drawContours(tempImage,contours,-1,Scalar(0,0,255),2,8);

    drawContours( InputOutputArray image, InputArrayOfArrays contours,
                                  int contourIdx, const Scalar& color,
                                  int thickness=1, int lineType=8,
                                  InputArray hierarchy=noArray(),
                                  int maxLevel=INT_MAX, Point offset=Point() );

    第一个参数是要绘制轮廓的图像,由于findContours会改变原图,所以这个绘制图像一般选择findContours前clone()的图像

    第二个参数是轮廓信息

    第三个参数若为负值则表示绘制所以轮廓,若为正数则表示 绘制第i个轮廓

    for(int i=0;i<counters.size();i++)

    {

    drawcounters(tempImage,counters,i,Scalar(0,0,255))//这个效果相当于循环counters.size()次每次绘制第i个图像,及绘制所有轮廓

    }

    第四个参数是绘制轮廓的线性颜色



    三:轮廓个数读取

    cout<<"轮廓个数"<<contours.size()<<endl;

    由于vector是向量,.size()表示的是向量的个数,而外层的vector表示的是所有轮廓信息,及counter.size()保存的是轮廓个数

    内层的vector向量保存的是单个轮廓的点信息,那么counters[i].size()表示的是单个轮廓的信息,及单个轮廓点的个数


    四:轮廓访问

    for(int i=0;i<counters.size();i++)//counters.size()表示的是轮廓的个数,counters相当于一个二维数组

    {

    for(int j=0;j<counters[i].size();j++)//counters[i].size()表示的是第i个轮廓所包含点的个数

    {

    circle(img,Point(counters[i][j].x,counter[i][j].y,100,Scalar(0,0,255))//counters[i][j]是第i个轮廓的第j个点,counters相当于二维数组

    )

    }

    }


    代码




    代码效果




    展开全文
  • 轮廓提取

    2019-10-28 22:37:05
    https://www.cnblogs.com/Anita9002/p/5332122.html
  • 实现图像中轮廓提取,包括外轮廓和内轮廓,为特征提取做准备。
  • opencv学习笔记二十一:轮廓提取

    千次阅读 2018-09-09 15:19:45
    轮廓:一个轮廓代表一系列的点(像素),这一系列的点构成一个有序的点集,所以可以把一个轮廓理解为一个有序的点集。 在opencv中,提供了一个函数返回一个有序的点集或者有序的点集的集合(指多个有序的点集),...
  • Opencv 轮廓提取

    千次阅读 2019-01-27 23:38:06
    增强——使边界轮廓更加明显 检测——选出边缘点 方法一 Canny算法 特点:轮廓细腻 #include&lt;opencv2\opencv.hpp&gt; #include&lt;opencv2\highgui\highgui.hpp&gt; using namespace std;...
  • 轮廓提取的原理

    2019-09-27 21:19:37
    我们知道,我们可以用各种算子,常见的canny算子等,对灰度图进行轮廓提取。 然后,进一步地,我们需要框选轮廓,那么是如何框选的呢?opencv有findcontour函数,但是我们想知道其中原理。 找出轮廓后,就可以用...
  • 轮廓提取后,它是用关键点组成的,下面提取出这些关键点。 1.先输出所有关键点的个数couttotal 2.for(int i=0;itotal;++i)  {  CvPoint* p = CV_GET_SEQ_ELEM(CvPoint,contour,i);  cout  }
  • 使用Matlab对二值图像进行轮廓提取

    万次阅读 2015-12-26 10:39:25
    参考资料 [1]
  • PS提取图形轮廓

    万次阅读 2017-07-09 14:37:23
    需要做图形轮廓相关的程序,所以记录一下其中的一些处理过程备忘,和程序没有太大关系。 打开文件启动PS软件,从文件——打开中,打开JPG格式的图片,如下: 查找边缘 滤镜——风格化——查找边缘 为了获得更...
  • 二值图像轮廓提取

    万次阅读 2010-12-09 14:29:00
    二值图像轮廓提取 <br /> 二值图像轮廓提取只需要挖空内部像素点即可。亮点的8个相邻像素点全部为亮点,则该点为内部点,反之为轮廓点。将所有内部点置为背景点,完成轮廓提取。 // 轮廓提取 // 1. ...
  • opencv中轮廓内部的像素点怎么提取

    万次阅读 2016-05-04 19:13:54
    opencv轮廓内面积的提取
  • 利用edge()函数提取图像轮廓,绘制出对象的边界和提取边界坐标信息,matlab实现代码如下: close all;clear all;clc; % 提取图像轮廓提取图像边缘 I = imread('yifu.jpg'); c = im2bw(I,graythresh(I)); ...
  • 使用matlab对图像轮廓进行提取

    万次阅读 热门讨论 2019-01-26 22:28:47
    然后转为二值图像使用bwperim()函数进行轮廓提取 同样我们可以借助edge()函数进行边界检测,算子可以选择canny,soble等等。  算子是什么呢? 简单的说就是一种运算方式,一种关系,一种映射。 广义的讲,...
  • Matlab实现图像的轮廓提取

    千次阅读 2018-05-10 22:18:47
    以二值图像circles.png(黑色背景白色前景)为例,根据掏空内部点算法,运用Matlab编程实现二值图像的轮廓提取。 I=imread('circles.png'); [M,N]=size(I); buffer=I; for i=2:M-1 for j=2:N-1 if(I(i,j)==255&...
  • 边缘检测与轮廓检测有什么区别?

    万次阅读 2018-11-23 15:54:43
    边缘检测是图像处理和计算机视觉中的基本问题,边缘检测的目的是标识数字图像中亮度变化明显的点。图像属性中的显著变化通常反映了属性...轮廓提取是提取出想要得到的轮廓 轮廓可能是边缘的一部分   图像处理开发...
  • OpenCV提取图像轮廓总结

    千次阅读 2014-06-06 14:42:33
    OpenCV提取图像轮廓总结
  • OpenCV中findContours轮廓提取一个边缘只对应的一个轮廓众所周知,图像查找轮廓可以用findContours函数解决,但是不知道大家发现一个问题没有,有时候输入图像,得到的结果会出现图像中一条边缘有查找到两个轮廓。...
1 2 3 4 5 ... 20
收藏数 24,820
精华内容 9,928
关键字:

轮廓提取