Computer Vision: Algorithms and ApplicationsのImage processing

来源:互联网 发布:protobuf c语言 编辑:程序博客网 时间:2024/05/16 08:31

实在是太喜欢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;elseC[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 averagelyfor(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;elseC[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 conersif(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 conerselse 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 areaelse{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=fh

卷积: 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,简单的角点检测器,同时寻找水平和垂直方向的二阶导数,这种算子不仅对正方形的角点有响应,而且对沿对角线方向的边缘也有响应
拉普拉斯算子是这样的1111-81111或者下面
0101-41010
在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 variablesMat 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 variablesMat 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这本书,发现这本书内容非常丰富,这里的文章我也会不断更新,如果你也很感兴趣,谢谢与我一起讨论^^

22 4
原创粉丝点击