精华内容
下载资源
问答
  • cv2.fitEllipse函数详解:ellipse参数并根据参数计算出椭圆焦点坐标 最近因为用到了这个函数,所以就趁着这个机会,好好顺顺,做下记录。 ellipse = cv2.fitEllipse(cnt) #(x, y), (a, b), angle = cv2.fitEllipse...

    cv2.fitEllipse函数详解:ellipse参数并根据参数计算出椭圆焦点坐标

    最近因为用到了这个函数,所以就趁着这个机会,好好顺顺,做下记录。

    ellipse = cv2.fitEllipse(cnt)
    
    #(x, y), (a, b), angle = cv2.fitEllipse(cnt)
    #ellipse =  [ (x, y) , (a, b), angle ]
    

    ellipse 为元组类型,其里面的参数为:

    • (x, y)代表椭圆中心点的位置
    • (a, b)代表长短轴长度,应注意a、b为长短轴的直径,而非半径
      angle 代表了中心旋转的角度

    椭圆参数角度效果

    旋转30°
    在这里插入图片描述
    旋转60°

    在这里插入图片描述

    旋转90°
    在这里插入图片描述

    旋转120°
    在这里插入图片描述

    旋转150°

    在这里插入图片描述

    旋转180°
    在这里插入图片描述

    旋转210°
    在这里插入图片描述
    从上图看应该是旋转180°以内和180°到360°是一样的。

    椭圆焦点坐标的计算

    对于一个旋转60°椭圆,如下:
    在这里插入图片描述
    则根据高中数学知识易得:
    在这里插入图片描述
    根据上图和其他参数,可以很简单的用下面的代码计算出来点的坐标:

    在这里插入图片描述
    计算代码如下:

    #画长宽
    res_ellipse = ((150,140),(150,100),60)
    ell_center_x = int(res_ellipse[0][0])
    ell_center_y = int(res_ellipse[0][1])
    
    ell_h_point1_x = int(ell_center_x - 0.5 * res_ellipse[1][0] * math.cos(res_ellipse[2]/ 180 * math.pi))
    ell_h_point1_y = int(ell_center_y - 0.5 * res_ellipse[1][0]* math.sin(res_ellipse[2]/ 180 * math.pi))
    ell_h_point2_x = int(ell_center_x + 0.5 * res_ellipse[1][0] * math.cos(res_ellipse[2]/ 180 * math.pi))
    ell_h_point2_y = int(ell_center_y + 0.5 * res_ellipse[1][0]* math.sin(res_ellipse[2]/ 180 * math.pi))
    
    ell_w_point1_x = int(ell_center_x - 0.5 * res_ellipse[1][1] * math.sin(res_ellipse[2]/ 180 * math.pi))
    ell_w_point1_y = int(ell_center_y + 0.5 * res_ellipse[1][1]* math.cos(res_ellipse[2]/ 180 * math.pi))
    ell_w_point2_x = int(ell_center_x + 0.5 * res_ellipse[1][1] * math.sin(res_ellipse[2]/ 180 * math.pi))
    ell_w_point2_y = int(ell_center_y - 0.5 * res_ellipse[1][1]* math.cos(res_ellipse[2]/ 180 * math.pi))
    
    
    cv2.line(img,(ell_h_point1_x,ell_h_point1_y),(ell_h_point2_x,ell_h_point2_y),(0,255,255),thickness=2)
    cv2.line(img,(ell_w_point1_x,ell_w_point1_y),(ell_w_point2_x,ell_w_point2_y),(0,255,255),thickness=2)
    
    
    

    最终效果图:
    在这里插入图片描述
    对于角度为240°(180+60)的椭圆,计算效果如下:
    在这里插入图片描述

    可以看出来没有影响。

    完整代码如下:

    其中"black.png“ 就是我随便截取的一个黑色背景。

    import os
    import numpy as np
    from matplotlib import pyplot as plt
    from PIL import Image
    import cv2
    from PIL import ImageEnhance
    import math
    def cv_show(name,file):
        cv2.imshow(name, file)
        cv2.waitKey(0)
        cv2.destroyAllWindows()
    # 椭圆参数的计算
    
    img = cv2.imread("black.png")
    #cv_show("res",img)
    cv2.ellipse(img, ((150,140),(150,100),60), (0, 255, 0), 2)
    
    cv_show("res",img)
    #画长宽
    
    res_ellipse = ((150,140),(150,100),60)
    ell_center_x = int(res_ellipse[0][0])
    ell_center_y = int(res_ellipse[0][1])
    
    ell_h_point1_x = int(ell_center_x - 0.5 * res_ellipse[1][0] * math.cos(res_ellipse[2]/ 180 * math.pi))
    ell_h_point1_y = int(ell_center_y - 0.5 * res_ellipse[1][0]* math.sin(res_ellipse[2]/ 180 * math.pi))
    ell_h_point2_x = int(ell_center_x + 0.5 * res_ellipse[1][0] * math.cos(res_ellipse[2]/ 180 * math.pi))
    ell_h_point2_y = int(ell_center_y + 0.5 * res_ellipse[1][0]* math.sin(res_ellipse[2]/ 180 * math.pi))
    
    ell_w_point1_x = int(ell_center_x - 0.5 * res_ellipse[1][1] * math.sin(res_ellipse[2]/ 180 * math.pi))
    ell_w_point1_y = int(ell_center_y + 0.5 * res_ellipse[1][1]* math.cos(res_ellipse[2]/ 180 * math.pi))
    ell_w_point2_x = int(ell_center_x + 0.5 * res_ellipse[1][1] * math.sin(res_ellipse[2]/ 180 * math.pi))
    ell_w_point2_y = int(ell_center_y - 0.5 * res_ellipse[1][1]* math.cos(res_ellipse[2]/ 180 * math.pi))
    
    
    cv2.line(img,(ell_h_point1_x,ell_h_point1_y),(ell_h_point2_x,ell_h_point2_y),(0,255,255),thickness=2)
    cv2.line(img,(ell_w_point1_x,ell_w_point1_y),(ell_w_point2_x,ell_w_point2_y),(0,255,255),thickness=2)
    
    cv_show("res_img",img)
    
    展开全文
  • An example program illustrates the use of cv::findContours and cv::drawContours WindowsQtContoursOutput.png Screenshot of the program #include “opencv2/imgproc.hpp” #include “opencv2/highgui.hpp” ...

    An example program illustrates the use of cv::findContours and cv::drawContours

    WindowsQtContoursOutput.png
    Screenshot of the program
    #include “opencv2/imgproc.hpp”
    #include “opencv2/highgui.hpp”
    #include <math.h>
    #include
    using namespace cv;
    using namespace std;
    static void help(char** argv)
    {
    cout
    << “\nThis program illustrates the use of findContours and drawContours\n”
    << “The original image is put up along with the image of drawn contours\n”
    << “Usage:\n”;
    cout
    << argv[0]
    << “\nA trackbar is put up which controls the contour level from -3 to 3\n”
    << endl;
    }
    const int w = 500;
    int levels = 3;
    vector<vector > contours;
    vector hierarchy;
    static void on_trackbar(int, void*)
    {
    Mat cnt_img = Mat::zeros(w, w, CV_8UC3);
    int _levels = levels - 3;
    drawContours( cnt_img, contours, _levels <= 0 ? 3 : -1, Scalar(128,255,255),
    3, LINE_AA, hierarchy, std::abs(_levels) );
    imshow(“contours”, cnt_img);
    }
    int main( int argc, char** argv)
    {
    cv::CommandLineParser parser(argc, argv, “{help h||}”);
    if (parser.has(“help”))
    {
    help(argv);
    return 0;
    }
    Mat img = Mat::zeros(w, w, CV_8UC1);
    //Draw 6 faces
    for( int i = 0; i < 6; i++ )
    {
    int dx = (i%2)250 - 30;
    int dy = (i/2)150;
    const Scalar white = Scalar(255);
    const Scalar black = Scalar(0);
    if( i == 0 )
    {
    for( int j = 0; j <= 10; j++ )
    {
    double angle = (j+5)CV_PI/21;
    line(img, Point(cvRound(dx+100+j
    10-80
    cos(angle)),
    cvRound(dy+100-90
    sin(angle))),
    Point(cvRound(dx+100+j10-30cos(angle)),
    cvRound(dy+100-30*sin(angle))), white, 1, 8, 0);
    }
    }
    ellipse( img, Point(dx+150, dy+100), Size(100,70), 0, 0, 360, white, -1, 8, 0 );
    ellipse( img, Point(dx+115, dy+70), Size(30,20), 0, 0, 360, black, -1, 8, 0 );
    ellipse( img, Point(dx+185, dy+70), Size(30,20), 0, 0, 360, black, -1, 8, 0 );
    ellipse( img, Point(dx+115, dy+70), Size(15,15), 0, 0, 360, white, -1, 8, 0 );
    ellipse( img, Point(dx+185, dy+70), Size(15,15), 0, 0, 360, white, -1, 8, 0 );
    ellipse( img, Point(dx+115, dy+70), Size(5,5), 0, 0, 360, black, -1, 8, 0 );
    ellipse( img, Point(dx+185, dy+70), Size(5,5), 0, 0, 360, black, -1, 8, 0 );
    ellipse( img, Point(dx+150, dy+100), Size(10,5), 0, 0, 360, black, -1, 8, 0 );
    ellipse( img, Point(dx+150, dy+150), Size(40,10), 0, 0, 360, black, -1, 8, 0 );
    ellipse( img, Point(dx+27, dy+100), Size(20,35), 0, 0, 360, white, -1, 8, 0 );
    ellipse( img, Point(dx+273, dy+100), Size(20,35), 0, 0, 360, white, -1, 8, 0 );
    }
    //show the faces
    namedWindow( “image”, 1 );
    imshow( “image”, img );
    //Extract the contours so that
    vector<vector > contours0;
    findContours( img, contours0, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE);
    contours.resize(contours0.size());
    for( size_t k = 0; k < contours0.size(); k++ )
    approxPolyDP(Mat(contours0[k]), contours[k], 3, true);
    namedWindow( “contours”, 1 );
    createTrackbar( “levels+3”, “contours”, &levels, 7, on_trackbar );
    on_trackbar(0,0);
    waitKey();
    return 0;
    }

    展开全文
  • Computer Vision: Algorithms and ApplicationsのImage processing

    千次阅读 多人点赞 2014-04-23 13:40:23
    实在是太喜欢Richard Szeliski的这本书了,每一章节(after chapter3)都详述了该研究方向比较新的成果,还有许多许多的reference,如果你感兴趣,完全可以看那些参考论文 Point operators(点算子) ...

    实在是太喜欢Richard Szeliski的这本书了,每一章节(after chapter3)都详述了该研究方向比较新的成果,还有许多许多的reference,如果你感兴趣,完全可以看那些参考论文

    Point operators(点算子)

    点运算是最简单的一类图像处理运算,如简单的对比度变换,亮度变换

    Pixel transform(像素变换)

    g(x) = af (x) +b    a和b有时被认为用来控制对比度和亮度,在我的opencv栏目有个例子是简单的对比度和亮度变换,用的就是这个公式

    g(x) = a(x)(x) + b(x)    a,b不一定是常数,可以是空间上的函数

    g(x) = (1 α)f0(x) + αf1(x)   α from0可以实现两幅图像的淡入淡出

    在OpenCV里有addWeighted( src1, alpha, src2, beta, 0.0, dst);这个函数,就是实现这个式子的

    g(x) = [f(x)]1    这是伽马校正属于幂变换,通常用于图像预处理阶段,对于大多数数字摄像机来说γ2.

    除了伽马校正,幂变换在控制对比度也很有用,可以取不同的γ试一试

    除了上面这些,我知道的还有:

    g(x) = L -1 -f(x)  灰度级属于[0,L-1]   这是图像反转  可用于增强嵌入与图像暗色区域的白色或灰色细节

    g(x) = clog(1+f(x))  对数变换


    Color transform(彩色变换)

    好像没有讲什么 = =


    Compositing and matting(合成与抠图)

    就是把一张图片里的东西(比如人)挖下来,然后合成到另一张图片中
    从一个场景中裁剪出前景物体叫抠图,将物体插入到另一幅图片中叫合成

    C=(1α)B+αF.  (覆盖算子)

    这个算子通过 (1 α )因子减弱了背景图像的影响,加入了对应于前景图像的彩色值(和不透明度)
    作者说,在这本书中,关心另外一个常用的算子(在Ex2中,等我做了再更新这里)

    Histogram equalization(直方图均衡化)

    我对这个非常熟悉,因为之前的图像处理课写过,还看过Pizer那有名的论文也用Java实现了CLAHE
    直方图均衡化的思想就是这样的。假设我有灰度级255的图像,但是都是属于[100,110]的灰度,图像对比度就很低,我应该尽可能拉到整个[0,255]
    下面是直方图均衡化的代码,有个累积函数的概念,其实很简单。
    我先计算出每个灰度级g(0),g(1)......g(255)点的个数,sum为图像width*height
    那么累计函数c(0) = g(0)/sum
    c(1) = (g(0)+g(1))/sum
    ......
    c(255) = 1
    public int[][] Histogram_Equalization(int[][] oldmat)
    	{
    		int[][] new_mat = new int[height][width];
    		int[] tmp = new int[256];
    		for(int i = 0;i < width;i++){
    			for(int j = 0;j < height;j++){
    				//System.out.println(oldmat[j][i]);
    				int index = oldmat[j][i];
    				tmp[index]++;
    			}
    		}
    		
    		float[] C = new float[256];
    		int total = width*height;
    		//计算累积函数
    		for(int i = 0;i < 256 ; i++){
    			if(i == 0)
    				C[i] = 1.0f * tmp[i] / total;
    			else
    				C[i] = C[i-1] + 1.0f * tmp[i] / total;
    		}
    		
    		for(int i = 0;i < width;i++){
    			for(int j = 0;j < height;j++){
    				new_mat[j][i] = (int)(C[oldmat[j][i]] * 255);
    				new_mat[j][i] = new_mat[j][i] + (new_mat[j][i] << 8) + (new_mat[j][i] << 16);
    				//System.out.println(new_mat[j][i]);
    			}
    		}
    		return new_mat;
    	}



    这是效果图,可以看到原来的图像被拉伸了

    自适应直方图均衡化
    AHE算法通过计算图像的局部直方图,然后重新分布亮度来来改变图像对比度。因此,该算法更适合于改进图像的局部对比度以及获得更多的图像细节。
    想像以下一幅图像,左上角是黑乎乎的一团,但是其他区域很正常,如果只用HE,那么黑乎乎的那团是没法有多大改进的。
    于是,你可以把那黑乎乎的一团当作一张图片,对那一部分进行HE,其实这就是AHE了,就是把图片分片处理,8*8是常用的选择。
    然后,你就可以写一个循环来操作,算法和HE是一模一样的,当然可以工作,只是速度比较慢。
    正如我下面代码所写的,利用双线性插值。
    我以前写CLAHE时候看的博客找不到了T_T   http://m.blog.csdn.net/blog/gududeyhc/8997009这里有但是远远没我以前看的那篇讲的清楚,如果你去看Pizer的论文估计要花很多的时间。下面是我用Java写的CLAHE.
    CLAHE比AHE多了裁剪补偿的操作
    /*
    	 * CLAHE
    	 * 自适应直方图均衡化
    	 */
    	public int[][] AHE(int[][] oldmat,int pblock)
    	{
    		int block = pblock;
    		//将图像均匀分成等矩形大小,8行8列64个块是常用的选择
    		int width_block = width/block;
    		int height_block = height/block;
    		
    		//存储各个直方图
    		int[][] tmp = new int[block*block][256];
    		//存储累积函数
    		float[][] C = new float[block*block][256];
    		
    		//计算累积函数
    		for(int i = 0 ; i < block ; i ++)
    		{
    			for(int j = 0 ; j < block ; j++)
    			{
    				int start_x = i * width_block;
    				int end_x = start_x + width_block;
    				int start_y = j * height_block;
    				int end_y = start_y + height_block;
    				int num = i+block*j;
    				int total = width_block * height_block;
    				for(int ii = start_x ; ii < end_x ; ii++)
    				{
    					for(int jj = start_y ; jj < end_y ; jj++)
    					{
    						int index = oldmat[jj][ii];
    						tmp[num][index]++;
    					}
    				}
    				
    				
    				//裁剪操作
    				int average = width_block * height_block / 255;
    				int LIMIT = 4 * average;
    				int steal = 0;
    				for(int k = 0 ; k < 256 ; k++)
    				{
    					if(tmp[num][k] > LIMIT){
    						steal += tmp[num][k] - LIMIT;
    						tmp[num][k] = LIMIT;
    					}
    					
    				}
    				
    				int bonus = steal/256;
    				//hand out the steals averagely
    				for(int k = 0 ; k < 256 ; k++)
    				{
    					tmp[num][k] += bonus;
    				}
    				
    				//计算累积分布直方图
    				for(int k = 0 ; k < 256 ; k++)
    				{
    					if( k == 0)
    						C[num][k] = 1.0f * tmp[num][k] / total;
    					else
    						C[num][k] = C[num][k-1] + 1.0f * tmp[num][k] / total;
    				}
    				
    			}
    		}
    		
    		int[][] new_mat = new int[height][width];
    		//计算变换后的像素值
    		//根据像素点的位置,选择不同的计算方法
    		for(int  i = 0 ; i < width; i++)
    		{
    			for(int j = 0 ; j < height; j++)
    			{
    				//four coners
    				if(i <= width_block/2 && j <= height_block/2)
    				{
    					int num = 0;
    					new_mat[j][i] = (int)(C[num][oldmat[j][i]] * 255);
    				}else if(i <= width_block/2 && j >= ((block-1)*height_block + height_block/2)){
    					int num = block*(block-1);
    					new_mat[j][i] = (int)(C[num][oldmat[j][i]] * 255);
    				}else if(i >= ((block-1)*width_block+width_block/2) && j <= height_block/2){
    					int num = block-1;
    					new_mat[j][i] = (int)(C[num][oldmat[j][i]] * 255);
    				}else if(i >= ((block-1)*width_block+width_block/2) && j >= ((block-1)*height_block + height_block/2)){
    					int num = block*block-1;
    					new_mat[j][i] = (int)(C[num][oldmat[j][i]] * 255);
    				}
    				
    				//four edges except coners
    				else if( i <= width_block/2 )
    				{
    					//线性插值
    					int num_i = 0;
    					int num_j = (j - height_block/2)/height_block;
    					int num1 = num_j*block + num_i;
    					int num2 = num1 + block;
    					float p =  (j - (num_j*height_block+height_block/2))/(1.0f*height_block);
    					float q = 1-p;
    					new_mat[j][i] = (int)((q*C[num1][oldmat[j][i]]+ p*C[num2][oldmat[j][i]])* 255);
    				}else if( i >= ((block-1)*width_block+width_block/2)){
    					//线性插值
    					int num_i = block-1;
    					int num_j = (j - height_block/2)/height_block;
    					int num1 = num_j*block + num_i;
    					int num2 = num1 + block;
    					float p =  (j - (num_j*height_block+height_block/2))/(1.0f*height_block);
    					float q = 1-p;
    					new_mat[j][i] = (int)((q*C[num1][oldmat[j][i]]+ p*C[num2][oldmat[j][i]])* 255);
    				}else if( j <= height_block/2 ){
    					//线性插值
    					int num_i = (i - width_block/2)/width_block;
    					int num_j = 0;
    					int num1 = num_j*block + num_i;
    					int num2 = num1 + 1;
    					float p =  (i - (num_i*width_block+width_block/2))/(1.0f*width_block);
    					float q = 1-p;
    					new_mat[j][i] = (int)((q*C[num1][oldmat[j][i]]+ p*C[num2][oldmat[j][i]])* 255);
    				}else if( j >= ((block-1)*height_block + height_block/2) ){
    					//线性插值
    					int num_i = (i - width_block/2)/width_block;
    					int num_j = block-1;
    					int num1 = num_j*block + num_i;
    					int num2 = num1 + 1;
    					float p =  (i - (num_i*width_block+width_block/2))/(1.0f*width_block);
    					float q = 1-p;
    					new_mat[j][i] = (int)((q*C[num1][oldmat[j][i]]+ p*C[num2][oldmat[j][i]])* 255);
    				}
    				
    				//inner area
    				else{
    					int num_i = (i - width_block/2)/width_block;
    					int num_j = (j - height_block/2)/height_block;
    					int num1 = num_j*block + num_i;
    					int num2 = num1 + 1;
    					int num3 = num1 + block;
    					int num4 = num2 + block;
    					float u = (i - (num_i*width_block+width_block/2))/(1.0f*width_block);
    					float v = (j - (num_j*height_block+height_block/2))/(1.0f*height_block);
    					new_mat[j][i] = (int)((u*v*C[num4][oldmat[j][i]] + 
    							(1-v)*(1-u)*C[num1][oldmat[j][i]] +
    							u*(1-v)*C[num2][oldmat[j][i]] +
    							v*(1-u)*C[num3][oldmat[j][i]]) * 255);
    
    				}
    				
    				new_mat[j][i] = new_mat[j][i] + (new_mat[j][i] << 8) + (new_mat[j][i] << 16);		
    			}
    		}
    		
    		return new_mat;
    		
    	}

    Application:Tonal adjustment(色调调整)

    Ex3.1

    Write a simple application to change the color balance of an imageby multiplying each color value by a different user-specified constant. If you want to getfancy, you can make this application interactive, with sliders. 

    我只是很简单地将颜色乘以系数,有slider,比较方便~~

    #include "opencv2/highgui/highgui.hpp"
    #include <iostream>
    
    using namespace cv;
    
    int alpha = 50;
    Mat image,new_image;
    static void change_color(int, void*)
    {
    	for( int y = 0; y < image.rows; y++ )
    			for( int x = 0; x < image.cols; x++ )
    				for( int c = 0; c < 3; c++ )
              			new_image.at<Vec3b>(y,x)[c] = saturate_cast<uchar>( alpha/50.0 *( image.at<Vec3b>(y,x)[c] ));
    
        imshow("Image", new_image);
    }
    
    int main( int, char** argv )
    {
       	image = imread( argv[1] );
       	new_image = Mat::zeros( image.size(), image.type() );
       	namedWindow("Image", 1);
    
    	createTrackbar( "pick:", "Image", &alpha, 100, change_color);
    	change_color(0, 0);
    
    	waitKey();
    	return 0;
    }


    Linear filtering(线性滤波)

    相关:  g = f h

    卷积:  g = f

    暂时不考虑边缘,所以8*8的图形进行相关或卷积操作后就得到6*6的图形
    因为我们的h(有时叫做核函数)是中心对称的,所以相关和卷积得到的结果是一样的
    那不一样呢?看下面的例子,用个一维的例子,{x,y}是核函数,{a,b,c,d,e}是数据
    这里构造核   与数据列表的卷积.
    In[1]:=
    Click for copyable input
    Out[1]=
    这里构造相关.
    In[2]:=
    Click for copyable input
    Out[2]=
    Padding(border effects)
    之前提到过,8*8的图像用3*3的核处理会成6*6,那么边界要怎么处理呢?

    • 0填充,很简单的处理方式
    • 常数填充
    • 夹取填塞(clamp),不断地复制边缘像素的值
    • 重叠填塞(wrap),以环状形态环绕图像进行循环
    • 镜像填塞(mirror),像素环绕图像边界进行镜像反射
    • 延长(extend),通过在边缘像素值中减去镜像信号的方式延长信号
    每种模式的公式要我们自己推导(Ex3.8)

    Separable filtering(可分离的滤波)

    二维卷积运算,更新一个像素点肯定需要 K 次运算(K是核函数的大小)
    文中提出了一种加速的方法,先用一维行向量进行卷积,再用一维列向量进行卷积,如果一个卷积核可以采用这种方法计算,就是可分离的。(这样子就只有2K次操作,很神奇吧)

    K =vhT  

    将卷积核K拆分成列向量v和行向量h
    当然,并不是所有K都能被拆分,下面举可以拆分的一些核函数

    最简单的平均滤波,[1,1,1......,1]* [1,1,1......,1] T   = K
    再看第3个高斯核,[1,4,6,4,1]* [1,4,6,4,1] T = K
    那么如何判断核函数是不是可分离的呢?文中告诉我们用奇异值分解的方法。
    我的想法是,例子都是中心对称的核函数,是不是必须要满足中心对称,只是一个朴素的想法罢了~

    原来,OpenCV帮我们实现了
    sepFilter2D()
    用分解的核函数对图像做卷积。
    首先,图像的每一行与一维的核 kernelX 做卷积;然后,运算结果的每一列与一维的核 kernelY 做卷积。

    Examples of linear filtering(线性滤波示例)

    如上图
    • box,非常简单,就是平均
    • bilinear,双线性核
    • Gaussian,非常有名,高斯核
    • Sobel算子,有效突出水平边缘(拉普拉斯算子也用于边缘提取,canny检测是边缘提取常用的算法)
    • corner,简单的角点检测器,同时寻找水平和垂直方向的二阶导数,这种算子不仅对正方形的角点有响应,而且对沿对角线方向的边缘也有响应
    拉普拉斯算子是这样的
    111
    1-81
    111
    或者下面
    010
    1-41
    010

    在Opencv里,都有各自对应的函数
    boxFilter()
    就是滑动窗口平均滤波的二维版。

    GaussianBlur()
    高斯平均,也就是高斯模糊。

    medianBlur()
    中值滤波,有效去除椒盐噪声。

    bilateralFilter()
    双线性滤波。
     
    下面我们来看看sobel算子和Laplace算子的边缘提取效果
    Sobel demo
    #include "opencv2/imgproc/imgproc.hpp"
    #include "opencv2/highgui/highgui.hpp"
    #include <stdlib.h>
    #include <stdio.h>
    
    using namespace cv;
    
    int main( int, char** argv )
    {
    
      Mat src, src_gray;
      Mat grad;
      const char* window_name = "Sobel Demo - Simple Edge Detector";
      int scale = 1;
      int delta = 0;
      int ddepth = CV_16S;
    
      /// Load an image
      src = imread( argv[1] );
    
      if( !src.data )
        { return -1; }
    
      GaussianBlur( src, src, Size(3,3), 0, 0, BORDER_DEFAULT );
    
      /// Convert it to gray
      cvtColor( src, src_gray, CV_RGB2GRAY );
    
      /// Create window
      namedWindow( window_name, CV_WINDOW_AUTOSIZE );
    
      /// Generate grad_x and grad_y
      Mat grad_x, grad_y;
      Mat abs_grad_x, abs_grad_y;
    
      /// Gradient X
      Sobel( src_gray, grad_x, ddepth, 1, 0, 3, scale, delta, BORDER_DEFAULT );
      convertScaleAbs( grad_x, abs_grad_x );
    
      /// Gradient Y
      Sobel( src_gray, grad_y, ddepth, 0, 1, 3, scale, delta, BORDER_DEFAULT );
      convertScaleAbs( grad_y, abs_grad_y );
    
      /// Total Gradient (approximate)
      addWeighted( abs_grad_x, 0.5, abs_grad_y, 0.5, 0, grad );
    
      imshow( window_name, grad );
    
      waitKey(0);
    
      return 0;
    }



    Laplace demo
    #include "opencv2/imgproc/imgproc.hpp"
    #include "opencv2/highgui/highgui.hpp"
    #include <stdlib.h>
    #include <stdio.h>
    
    using namespace cv;
    
    int main( int, char** argv )
    {
    
      Mat src, src_gray, dst;
      int kernel_size = 3;
      int scale = 1;
      int delta = 0;
      int ddepth = CV_16S;
      const char* window_name = "Laplace Demo";
    
      /// Load an image
      src = imread( argv[1] );
    
      if( !src.data )
        { return -1; }
    
      /// Remove noise by blurring with a Gaussian filter
      GaussianBlur( src, src, Size(3,3), 0, 0, BORDER_DEFAULT );
    
      /// Convert the image to grayscale
      cvtColor( src, src_gray, CV_RGB2GRAY );
    
      /// Create window
      namedWindow( window_name, CV_WINDOW_AUTOSIZE );
    
      /// Apply Laplace function
      Mat abs_dst;
    
      Laplacian( src_gray, dst, ddepth, kernel_size, scale, delta, BORDER_DEFAULT );
      convertScaleAbs( dst, abs_dst );
    
      /// Show what you got
      imshow( window_name, abs_dst );
    
      waitKey(0);
    
      return 0;
    }


    如果你想实现自己的滤波器,在OpenCV里要怎么做呢,很简单
    filter2D(image, image, image.depth(), (Mat<float>(3,3)<<-1, -1, -1, -1, 9, -1, -1, -1, -1), Point(1,1), 128);
    构造了一个如下所示的核对图像做卷积:
    -1 -1 -1
    -1 9 -1
    -1 -1 -1
    核的锚点在 (1,1) 位置,卷积之后每个像素加上 128.
    核的锚点表示一个被滤波的点在核内的位置。 锚点应该处于核内部。缺省值 (-1,-1) 表示锚点在核中心。
    #include "opencv2/imgproc/imgproc.hpp"
    #include "opencv2/highgui/highgui.hpp"
    #include <stdlib.h>
    #include <stdio.h>
    
    using namespace cv;
    
    int main ( int, char** argv )
    {
      /// Declare variables
      Mat src, dst;
    
      Mat kernel;
      Point anchor;
      double delta;
      int ddepth;
      int kernel_size;
      const char* window_name = "filter2D Demo";
    
      int c;
    
      /// Load an image
      src = imread( argv[1] );
    
      if( !src.data )
        { return -1; }
    
      /// Create window
      namedWindow( window_name, CV_WINDOW_AUTOSIZE );
    
      /// Initialize arguments for the filter
      anchor = Point( -1, -1 );
      delta = 0;
      ddepth = -1;
    
      /// Loop - Will filter the image with different kernel sizes each 0.5 seconds
      int ind = 0;
      for(;;)
           {
             c = waitKey(500);
             /// Press 'ESC' to exit the program
             if( (char)c == 27 )
               { break; }
    
             /// Update kernel size for a normalized box filter
             kernel_size = 3 + 2*( ind%5 );
             kernel = Mat::ones( kernel_size, kernel_size, CV_32F )/ (float)(kernel_size*kernel_size);
    
             /// Apply filter
             filter2D(src, dst, ddepth , kernel, anchor, delta, BORDER_DEFAULT );
             imshow( window_name, dst );
             ind++;
           }
    
      return 0;
    }

    Band-pass and steerable filters(带通和导向滤波器)

    Sobel和Corner算子是带通和导向滤波器的简单例子
    可以按如下方式构造更精细的核:首先用高斯滤波器平滑图像,再用一阶或二阶导数来构造更精细的核,跟之前的sobel demo,Laplace demo做得差不多
    文中的意思是高阶的导向滤波器更适合检测边缘的结构

    Summed area table(integral image)积分图像

    什么是积分图像?
    积分图像就是一个新的table s(width*height)
    s(2,2) = f(1,1)+f(1,2)+f(2,1)+f(2,2)
    s中的每个点都是左上方所有数字与自己之和

    很容易发现有 s(i, j) = s(i1, j) +s(i, j1)s(i1, j1) +f(i, j)

    有了sum table后,我们要求(1,1)到(4,4)这个矩形的积分就很快了,只利用上面4个紫色的点,48+3-13-14 = 24
    很明显,积分图像起到一个加快运算的功能,那么应用在哪呢?
    • 人脸检测利用积分图像来计算简单的多尺度上的底层特征
    • 立体视觉和运动算法中差分平方和(SSD)的求和计算
    • 可分离的移动平均滤波器

    OpenCV有自带的计算积分图的函数integral 提供了更多选项,sum是和,sqsum是平方和图像,tilted是旋转45度的和

    • sum: the sum summation integral image

    • sqsum: the square sum integral image

    • tiltedimage is rotated by 45 degrees and then its integral is calculated


    拭目以待

    Recursive filtering(递归滤波器)
    sum table的公式  s ( i, j ) =  s ( −  1 , j ) +  s ( i, j  −  1)  −  s ( −  1 , j  −  1) +  f ( i, j )是递归滤波器的一个例子
    递归滤波器是指输出值取决于前一个滤波器的输出值

    More neighborhood operators(更多的领域算子)

    线性滤波可以实现大量的图像变换,然而非线性滤波有时可以达到更好的效果

    Non linear filtering(非线性滤波)


    median filter 中值滤波
    中值滤波器选择邻域内的中值作为输出,当噪声(散粒噪声)变化幅度很大,中值滤波就很好用,但是对于高斯噪声就不好用了

    Bilateral filter 双边滤波 
    是结合图像的空间邻近度和像素值相似度的一种折衷处理,同时考虑空域信息和灰度相似性,达到保边去噪的目的。具有简单、非迭代、局部的特点。 双边滤波器的好处是可以做边缘保存(edge preserving),一般过去用的维纳滤波或者高斯滤波去降噪,都会较明显地模糊边缘,对于高频细节的保护效果并不明显。双边滤波器顾名思义比 高斯滤波 多了一个高斯方差sigma-d,它是基于空间分布的高斯滤波 函数 ,所以在边缘附近,离的较远的像素不会太多影响到边缘上的像素值,这样就保证了边缘附近像素值的保存。但是由于保存了过多的高频信息,对于彩色图像里的高频噪声,双边滤波器不能够干净的滤掉,只能够对于低频信息进行较好的滤波

    //OpenCV双边滤波
       //src:输入图像
       //dst:输入图像
       //滤波模板半径
       //颜色空间标准差
       //坐标空间标准差
       bilateralFilter(src,dst,5,10.0,2.0);
    //关于滤波,还可以参考这里


    Iterated adaptive smoothing and anisotropic diffusion(迭代自适应平滑和各向异性扩散)

    目前还没看懂 = =

    Morphology(形态学)

    腐蚀,膨胀
    #include "opencv2/imgproc/imgproc.hpp"
    #include "opencv2/highgui/highgui.hpp"
    #include <stdlib.h>
    #include <stdio.h>
    
    using namespace cv;
    
    /// Global variables
    Mat src, erosion_dst, dilation_dst;
    
    int erosion_elem = 0;
    int erosion_size = 0;
    int dilation_elem = 0;
    int dilation_size = 0;
    int const max_elem = 2;
    int const max_kernel_size = 21;
    
    /** Function Headers */
    void Erosion( int, void* );
    void Dilation( int, void* );
    
    /**
     * @function main
     */
    int main( int, char** argv )
    {
      /// Load an image
      src = imread( argv[1] );
    
      if( !src.data )
        { return -1; }
    
      /// Create windows
      namedWindow( "Erosion Demo", CV_WINDOW_AUTOSIZE );
      namedWindow( "Dilation Demo", CV_WINDOW_AUTOSIZE );
      cvMoveWindow( "Dilation Demo", src.cols, 0 );
    
      /// Create Erosion Trackbar
      createTrackbar( "Element:\n 0: Rect \n 1: Cross \n 2: Ellipse", "Erosion Demo",
              &erosion_elem, max_elem,
              Erosion );
    
      createTrackbar( "Kernel size:\n 2n +1", "Erosion Demo",
              &erosion_size, max_kernel_size,
              Erosion );
    
      /// Create Dilation Trackbar
      createTrackbar( "Element:\n 0: Rect \n 1: Cross \n 2: Ellipse", "Dilation Demo",
              &dilation_elem, max_elem,
              Dilation );
    
      createTrackbar( "Kernel size:\n 2n +1", "Dilation Demo",
              &dilation_size, max_kernel_size,
              Dilation );
    
      /// Default start
      Erosion( 0, 0 );
      Dilation( 0, 0 );
    
      waitKey(0);
      return 0;
    }
    
    /**
     * @function Erosion
     */
    void Erosion( int, void* )
    {
      int erosion_type = 0;
      if( erosion_elem == 0 ){ erosion_type = MORPH_RECT; }
      else if( erosion_elem == 1 ){ erosion_type = MORPH_CROSS; }
      else if( erosion_elem == 2) { erosion_type = MORPH_ELLIPSE; }
    
      Mat element = getStructuringElement( erosion_type,
                           Size( 2*erosion_size + 1, 2*erosion_size+1 ),
                           Point( erosion_size, erosion_size ) );
      /// Apply the erosion operation
      erode( src, erosion_dst, element );
      imshow( "Erosion Demo", erosion_dst );
    }
    
    /**
     * @function Dilation
     */
    void Dilation( int, void* )
    {
      int dilation_type = 0;
      if( dilation_elem == 0 ){ dilation_type = MORPH_RECT; }
      else if( dilation_elem == 1 ){ dilation_type = MORPH_CROSS; }
      else if( dilation_elem == 2) { dilation_type = MORPH_ELLIPSE; }
    
      Mat element = getStructuringElement( dilation_type,
                           Size( 2*dilation_size + 1, 2*dilation_size+1 ),
                           Point( dilation_size, dilation_size ) );
      /// Apply the dilation operation
      dilate( src, dilation_dst, element );
      imshow( "Dilation Demo", dilation_dst );
    }

    开运算

    dst=open(src,element)=dilate(erode(src,element),element)

    闭运算

    dst=close(src,element)=erode(dilate(src,element),element)

    形态梯度

    dst=morph_grad(src,element)=dilate(src,element)-erode(src,element)

    "顶帽"

    dst=tophat(src,element)=src-open(src,element)

    "黑帽"

    dst=blackhat(src,element)=close(src,element)-src

    临时图像 temp 在形态梯度以及对“顶帽”和“黑帽”操作时的 in-place 模式下需要。


    #include "opencv2/imgproc/imgproc.hpp"
    #include "opencv2/highgui/highgui.hpp"
    #include <stdlib.h>
    #include <stdio.h>
    
    using namespace cv;
    
    /// Global variables
    Mat src, dst;
    
    int morph_elem = 0;
    int morph_size = 0;
    int morph_operator = 0;
    int const max_operator = 4;
    int const max_elem = 2;
    int const max_kernel_size = 21;
    
    const char* window_name = "Morphology Transformations Demo";
    
    
    /** Function Headers */
    void Morphology_Operations( int, void* );
    
    /**
     * @function main
     */
    int main( int, char** argv )
    {
      /// Load an image
      src = imread( argv[1] );
    
      if( !src.data )
        { return -1; }
    
      /// Create window
      namedWindow( window_name, CV_WINDOW_AUTOSIZE );
    
      /// Create Trackbar to select Morphology operation
      createTrackbar("Operator:\n 0: Opening - 1: Closing  \n 2: Gradient - 3: Top Hat \n 4: Black Hat", window_name, &morph_operator, max_operator, Morphology_Operations );
    
      /// Create Trackbar to select kernel type
      createTrackbar( "Element:\n 0: Rect - 1: Cross - 2: Ellipse", window_name,
              &morph_elem, max_elem,
              Morphology_Operations );
    
      /// Create Trackbar to choose kernel size
      createTrackbar( "Kernel size:\n 2n +1", window_name,
              &morph_size, max_kernel_size,
              Morphology_Operations );
    
      /// Default start
      Morphology_Operations( 0, 0 );
    
      waitKey(0);
      return 0;
    }
    
    /**
     * @function Morphology_Operations
     */
    void Morphology_Operations( int, void* )
    {
    
      // Since MORPH_X : 2,3,4,5 and 6
      int operation = morph_operator + 2;
    
      Mat element = getStructuringElement( morph_elem, Size( 2*morph_size + 1, 2*morph_size+1 ), Point( morph_size, morph_size ) );
    
      /// Apply the specified morphology operation
      morphologyEx( src, dst, operation, element );
      imshow( window_name, dst );
    }



    Reference

    • Richard Szeliski 《Computer Vision: Algorithms and Applications》
    • http://blog.csdn.net/xiaowei_cqu/article/details/7785365
    • 《The OpenCV Reference Manual 》 Release 2.4.7



    我最近刚开始看Computer Vision: Algorithms and Applications这本书,发现这本书内容非常丰富,这里的文章我也会不断更新,如果你也很感兴趣,谢谢与我一起讨论^^

    展开全文
  • 在OpenCV3中的绘图详解中,我们详细地介绍了opencv3中所支持的各种几何形状的绘制,其中矩形的绘制接口为cv::rectangle(),但是这个接口只能用于绘制正常矩形(即cv::Rect定义的矩形,长是水平,高是竖直),但不...

    OpenCV3中的绘图详解中,我们详细地介绍了opencv3中所支持的各种几何形状的绘制,其中矩形的绘制接口为cv::rectangle(),但是这个接口只能用于绘制正常矩形(即cv::Rect定义的矩形,长是水平,高是竖直),但不支持旋转矩形(cv::RotateRect)的绘制。比如我们使用如下代码,通过若干点来拟合椭圆(cv::fitEllipse()),拟合出的椭圆是以椭圆的外接矩形呈现的,这个椭圆的外接矩形就是一个旋转矩形。


    	//创建一个用于绘制图像的空白图  
    	cv::Mat image = cv::Mat::ones(480, 640, CV_8UC3);
    	//设置蓝色背景
    	image.setTo(cv::Scalar(100, 0, 0));
    
    	//输入拟合点  
    	std::vector<cv::Point> points;
    
    	points.push_back(cv::Point(200, 240));
    	points.push_back(cv::Point(300, 400));
    	points.push_back(cv::Point(400, 360));
    	points.push_back(cv::Point(500, 300));
    	points.push_back(cv::Point(500, 200));
    	points.push_back(cv::Point(300, 150));
    
    	//将拟合点绘制到空白图上  
    	for (int i = 0; i < points.size(); i++)
    	{
    		cv::circle(image, points[i], 5, cv::Scalar(0, 0, 255), 2, 8, 0);
    	}
    
    	//获取拟合椭圆的外包围矩形
    	cv::RotatedRect rotate_rect = cv::fitEllipse(points);
    	//绘制拟合椭圆
    	cv::ellipse(image, rotate_rect, cv::Scalar(0, 255, 255), 2, 8);
    
    	cv::imshow("image", image);
    	cv::waitKey(0);



    从代码中可以看出,我们可以使用cv::ellipse()来绘制椭圆。但是,如果我们想将这个外接的旋转矩形也绘制出来的话,该怎么操作?opencv中有没有提供专门用于绘制旋转矩形的函数?很遗憾,没有。


    下面介绍两种种解决旋转矩形的绘制问题的方案。


    1、使用cv::line()逐条边绘制

    代码如下:

    	//获取旋转矩形的四个顶点
    	cv::Point2f* vertices = new cv::Point2f[4];
    	rotate_rect.points(vertices);
    
    	//逐条边绘制
    	for (int j = 0; j < 4; j++)
    	{
    		cv::line(image, vertices[j], vertices[(j + 1) % 4], cv::Scalar(0, 255, 0));
    	}


    结果如下:



    2、使用cv::drawContours()函数进行绘制

    代码如下:

    	//获取旋转矩形的四个顶点
    	cv::Point2f* vertices = new cv::Point2f[4];
    	rotate_rect.points(vertices);
    	
    	std::vector<cv::Point> contour;
    
    	for (int i = 0; i < 4; i++)
    	{
    		contour.push_back(vertices[i]);
    	}
    
    	std::vector<std::vector<cv::Point>> contours;
    	contours.push_back(contour);
    	cv::drawContours(image, contours, 0, cv::Scalar(255, 255, 0), 1);


    结果如下:




    2017.04.11

    展开全文
  • cv::Mat掩膜操作与多边形roi区域的提取 本篇博文用于记录 cv::Mat 掩膜操作的学习心得,并附上一种基于掩膜操作的多边形roi区域提取方法。 cv::Mat 的掩膜操作(mask) 这里介绍cv::Mat常用的两个掩膜操作方法: ...
  • 首先定位cv::cvtColor 函数,找到其文件,
  • 目录 1.绘制线段:cv2.line(img,pt1,pt2,color,thickness=None,lineType=None,shift=None) 2.绘制矩形:cv2.rectangle(img,pt1,pt2,color,thickness=None,lineType=None,shift=None) ...4.椭圆绘制:cv2.ellipse(i.
  • [OpenCV4] cv :: threshold() 阈值函数

    千次阅读 2019-11-09 21:32:43
    官方文档: 参考链接 1.函数原型 ...The function applies fixed-level thresholding to a multiple-channel array.... could be also used for this purpose) or for removing...using namespace cv;    
  • using namespace cv; using namespace std; Mat src, erosion_dst, dilation_dst; int erosion_elem = 0; int erosion_size = 0; int dilation_elem = 0; int dilation_size = 0; int const max_elem = 2; int const...
  • cv2.ellipse()

    千次阅读 2021-02-15 17:51:09
    OpenCV-Python是旨在解决计算机视觉问题的Python绑定库。cv2.ellipse()方法用于在任何图像上绘制椭圆。 cv2.ellipse(image, centerCoordinates, axesLength, angle, startAngle, endAngle, color [, thickness[, ...
  • 刚刚接触Python以及OpenCV,通过敲本书中的代码来学习计算机视觉。在按照书中代码进行实现时,发现有些部分总是没有作者实现的好(代码是完全一些样的)。经过反复琢磨对比试验后,明白了其中的原因:作者使用的摄像头...
  • ​OpenCV是一个用于图像处理、分析、机器视觉方面...OpenCV轻量级而且高效,它是由一系列 C 函数和少量 C++ 类构成,同时提供了Python、Ruby、MATLAB等语言的接口,实现了图像处理和计算机视觉方面的很多通用算法。 ...
  • ,返回值也是RotatedRect,可以用ellipse函数画出对应的椭圆 如果想根据多边形的轮廓信息得到多边形的多阶矩,可以使用类 moments ,这个类可以得到多边形和光栅形状的3阶以内的所有矩,类内有变量m00,m10,m01...
  • ,返回值也是RotatedRect,可以用ellipse函数画出对应的椭圆 如果想根据多边形的轮廓信息得到多边形的多阶矩,可以使用类 moments ,这个类可以得到多边形和光栅形状的3阶以内的所有矩,类内有变量m00,m10,...
  • opencv::绘制-基本几何

    2019-09-23 17:00:09
    画线 cv::line(LINE_4\LINE_8\LINE_AA)...画椭圆 cv::ellipse 画矩形 cv::rectangle 画圆 cv::circle 画填充 cv::fillPoly putText(bgImage, "Hello OpenCV 你好 ", Point(300, 300), CV_FONT_HERSHEY_...
  • using namespace cv;#include "opencv2/nonfree/nonfree.hpp"//SIFT相关#include "opencv2/legacy/legacy.hpp"//匹配器相关#include &lt;iostream&gt;using namespace std;int ma...
  • 学习OpenCV3:在空白图片上画虚线

    千次阅读 2020-06-17 08:32:30
    一、背景 ...cv::ellipse() // 画一个椭圆,可以倾斜,或者只有部分圆弧 cv::ellipse2Poly() // 计算一个近似椭圆的多边形 cv::fillConvexPoly() // 画一个填充的简单多边形 cv::fillPoly() // 画.
  • 前言 这是我《OpenCV:从零到一...画椭圆cv::ellipse 画矩形cv::rectangle 画圆cv::circle 画填充cv::fillPoly 随机生成与绘制文本 RNG类 生成高斯随机数RNG.gaussian (double sigma) 生成正态分布随机数RNG.uniform
  • #include &lt;stdio.h&gt; #include &lt;opencv2/opencv.hpp&gt; ...using namespace std;...using namespace cv;... //line ellipse rectangle circle fillPoly Mat Pic = Mat::ze...
  • OpenCV示例学习(二): 基本图形绘制算子:line(),circle(),fillPoly(), ellipse() #include <opencv2/opencv.hpp> using namespace cv; using namespace std; #define WINDOW_NAME1 "【绘制图1】" //为...
  • 主要有cv2.line()//画线, cv2.circle()//画圆, cv2.rectangle()//长方形,cv2.ellipse()//椭圆, cv2.putText()//文字绘制 主要参数 img:源图像 color:需要传入的颜色 thickness:线条的粗细,默认值是1...
  • Ellipse ROI causing an error

    2020-12-25 17:38:47
    The 5 parameter cv2.ellipse() takes as input a box which will inscribe an ellipse, while the 8 parameter cv2.ellipse() instead takes as input several dimensions of the ellipse itself. We would ...
  • 主要有cv2.line()//画线, cv2.circle()//画圆, cv2.rectangle()//长方形,cv2.ellipse()//椭圆, cv2.putText()//文字绘制 主要参数 img:源图像 color:需要传入的颜色 thickness:线条的粗细,默认值是1 ...
  • error_ellipse

    2015-05-23 01:05:56
    二维空间协方差矩阵可视化为一个误差椭圆的matlab和C++代码,C++代码应用到了opencv的库函数,所以如果需要运行这个代码需要配置opencv环境。
  • 繪圖(line、rectangle、circle、ellipse、polylines、putText) OpenCV有函式方便我們繪圖,這邊依序介紹如何繪製線、矩形、圓、橢圓、多角形、文字在影像上,其他圖形請參考OpenCV文件。 內文索引 [隱藏] 1 ...
  • 10、OpenCV 画椭圆 —— cv.ellipse()

    千次阅读 2020-09-18 16:36:18
    /4*3), int(imgBgr.shape[0]/4*3)) ptEllAxis2 = (int(imgBgr.shape[1]/8), int(imgBgr.shape[0]/10)) # 画椭圆 imgRet1 = cv.ellipse(imgBgr, ptEllCenter1, ptEllAxis1, 90, 0, 360, (0, 255, 0), 3) imgRet2 = cv...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 6,904
精华内容 2,761
关键字:

cv::ellipse