参考:
1、https://docs.opencv.org/3.2.0/
2、https://github.com/opencv/opencv/
在本教程中,您将学习如何使用OpenCV函数应用各种线性滤镜来平滑图像,例如:
假设图像是1D,您可以注意到位于中间的像素将具有最大的权重。 其邻居的权重随着它们与中心像素之间的空间距离的增加而减小。
请记住,二维高斯可以表示为:
中值滤波器贯穿信号的每个元素(在这种情况下是图像),并用每个像素的相邻像素的中值(位于评估像素周围的正方形邻域)替换。
#include "opencv2/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
using namespace std;
using namespace cv;
int DELAY_CAPTION = 1500;
int DELAY_BLUR = 100;
int MAX_KERNEL_LENGTH = 31;
Mat src; Mat dst;
char window_name[] = "Smoothing Demo";
int display_caption( const char* caption );
int display_dst( int delay );
int main( void )
{
namedWindow( window_name, WINDOW_AUTOSIZE );
src = imread( "../data/lena.jpg", IMREAD_COLOR );
if( display_caption( "Original Image" ) != 0 ) { return 0; }
dst = src.clone();
if( display_dst( DELAY_CAPTION ) != 0 ) { return 0; }
if( display_caption( "Homogeneous Blur" ) != 0 ) { return 0; }
for ( int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2 )
{ blur( src, dst, Size( i, i ), Point(-1,-1) );
if( display_dst( DELAY_BLUR ) != 0 ) { return 0; } }
if( display_caption( "Gaussian Blur" ) != 0 ) { return 0; }
for ( int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2 )
{ GaussianBlur( src, dst, Size( i, i ), 0, 0 );
if( display_dst( DELAY_BLUR ) != 0 ) { return 0; } }
if( display_caption( "Median Blur" ) != 0 ) { return 0; }
for ( int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2 )
{ medianBlur ( src, dst, i );
if( display_dst( DELAY_BLUR ) != 0 ) { return 0; } }
if( display_caption( "Bilateral Blur" ) != 0 ) { return 0; }
for ( int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2 )
{ bilateralFilter ( src, dst, i, i*2, i/2 );
if( display_dst( DELAY_BLUR ) != 0 ) { return 0; } }
display_caption( "End: Press a key!" );
waitKey(0);
return 0;
}
int display_caption( const char* caption )
{
dst = Mat::zeros( src.size(), src.type() );
putText( dst, caption,
Point( src.cols/4, src.rows/2),
FONT_HERSHEY_COMPLEX, 1, Scalar(255, 255, 255) );
imshow( window_name, dst );
int c = waitKey( DELAY_CAPTION );
if( c >= 0 ) { return -1; }
return 0;
}
int display_dst( int delay )
{
imshow( window_name, dst );
int c = waitKey ( delay );
if( c >= 0 ) { return -1; }
return 0;
}
1、让我们来看看只涉及平滑过程的OpenCV函数,因为现在已经知道了其余部分。
2、Normalized Block Filter:
OpenCV提供cv :: blur函数来执行此滤镜的平滑处理。
for ( int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2 )
{ blur( src, dst, Size( i, i ), Point(-1,-1) );
if( display_dst( DELAY_BLUR ) != 0 ) { return 0; } }
我们指定了4个参数(更多细节,请参考参考资料):
3、Gaussian Filter:
它由cv :: GaussianBlur函数执行:
for ( int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2 )
{ GaussianBlur( src, dst, Size( i, i ), 0, 0 );
if( display_dst( DELAY_BLUR ) != 0 ) { return 0; } }
这里我们使用4个参数(更多细节,请查看OpenCV参考):
4、Median Filter:
这个过滤器是由cv :: medianBlur函数提供的:
for ( int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2 )
{ medianBlur ( src, dst, i );
if( display_dst( DELAY_BLUR ) != 0 ) { return 0; } }
我们使用三个参数:
5、Bilateral Filter
由OpenCV提供的函数cv :: bilateralFilter
for ( int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2 )
{ bilateralFilter ( src, dst, i, i*2, i/2 );
if( display_dst( DELAY_BLUR ) != 0 ) { return 0; } }
我们使用5个参数:
应用两个非常常见的形态学算子:膨胀和侵蚀。 为此,您将使用以下OpenCV函数:
背景(明亮)在信件的黑色区域扩张。
为了更好地把握这个想法,避免可能的混淆,在这个又一个例子中,我们倒置了原来的图像,比如白色的对象现在是这个字母。 我们用尺寸为3×3的矩形结构元素进行了两次扩张。
扩张使物体在白色更大。
以相同的方式,在倒置的原始图像上进行侵蚀操作所得到的对应图像(具有尺寸为3×3的矩形结构元素的两次侵蚀):
侵蚀使物体变白。
本教程代码的显示如下。 您也可以从这里下载
#include "opencv2/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
using namespace cv;
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;
void Erosion( int, void* );
void Dilation( int, void* );
int main( int, char** argv )
{
src = imread( argv[1], IMREAD_COLOR );
if( src.empty() )
{ return -1; }
namedWindow( "Erosion Demo", WINDOW_AUTOSIZE );
namedWindow( "Dilation Demo", WINDOW_AUTOSIZE );
moveWindow( "Dilation Demo", src.cols, 0 );
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 );
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 );
Erosion( 0, 0 );
Dilation( 0, 0 );
waitKey(0);
return 0;
}
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 ) );
erode( src, erosion_dst, element );
imshow( "Erosion Demo", erosion_dst );
}
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 ) );
dilate( src, dilation_dst, element );
imshow( "Dilation Demo", dilation_dst );
}
1、所显示的大部分内容都是由您所知(如果您有任何疑问,请参阅前几节中的教程)。 我们来看看程序的一般结构:
我们来分析这两个函数:
2、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 ) );
erode( src, erosion_dst, element );
imshow( "Erosion Demo", erosion_dst );
}
Mat element = getStructuringElement( erosion_type,
Size( 2*erosion_size + 1, 2*erosion_size+1 ),
Point( erosion_size, erosion_size ) );
我们可以为我们的内核选择三种形状的任何一种:
- 矩形框:MORPH_RECT
- 十字架:MORPH_CROSS
- 椭圆形:MORPH_ELLIPSE
然后,我们只需要指定我们的内核和定位点的大小。 如果未指定,则假定位于中心。
就这些。 我们准备好进行我们形象的侵蚀。
此外,还有另一个参数,可以让您一次执行多个腐蚀(迭代)。 不过,我们并没有在这个简单的教程中使用它。 您可以查看参考资料了解更多详情。
3、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 ) );
dilate( src, dilation_dst, element );
imshow( "Dilation Demo", dilation_dst );
}
在本教程中,您将学习如何:
在之前的教程中,我们介绍了两个基本的形态学操作:
基于这两个,我们可以对我们的图像进行更复杂的转换。 这里我们简要地讨论OpenCV提供的5个操作:
为了清楚起见,我们在同一原始图像上进行了开放操作(7×7矩形结构化元素),但是如白色的对象现在是字母。
在倒像上,我们进行了Closing操作(7x7矩形结构元素):
本教程代码的显示如下。 您也可以从这里下载
#include "opencv2/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
using namespace cv;
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";
void Morphology_Operations( int, void* );
int main( int, char** argv )
{
src = imread( argv[1], IMREAD_COLOR ); // Load an image
if( src.empty() )
{ return -1; }
namedWindow( window_name, WINDOW_AUTOSIZE ); // Create window
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 );
createTrackbar( "Element:\n 0: Rect - 1: Cross - 2: Ellipse", window_name,
&morph_elem, max_elem,
Morphology_Operations );
createTrackbar( "Kernel size:\n 2n +1", window_name,
&morph_size, max_kernel_size,
Morphology_Operations );
Morphology_Operations( 0, 0 );
waitKey(0);
return 0;
}
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 ) );
morphologyEx( src, dst, operation, element );
imshow( window_name, dst );
}
1、我们来看看程序的一般结构:
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 );
createTrackbar( "Element:\n 0: Rect - 1: Cross - 2: Ellipse", window_name,
&morph_elem, max_elem,
Morphology_Operations );
createTrackbar( "Kernel size:\n 2n +1", window_name,
&morph_size, max_kernel_size,
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 ) );
morphologyEx( src, dst, operation, element );
imshow( window_name, dst );
}
我们可以观察到执行形态转换的关键功能是cv :: morphologyEx。 在这个例子中,我们使用了四个参数(剩下的就是默认值):
int operation = morph_operator + 2;
应用两个非常常见的形态算子(即膨胀和侵蚀),创建自定义内核,以便在水平和垂直轴上提取直线。 为此,您将使用以下OpenCV函数:
在一个例子中,您的目标是从音乐表中提取音乐表。
形态学是一组图像处理操作,其基于也称为内核的预定义的结构元素来处理图像。 输出图像中的每个像素的值基于输入图像中的对应像素与其邻域的比较。 通过选择内核的大小和形状,可以构建对输入图像的特定形状敏感的形态学操作。
两个最基本的形态学操作是扩张和侵蚀。 膨胀将像素添加到图像中对象的边界,而侵蚀则完全相反。 添加或去除的像素数量分别取决于用于处理图像的结构元素的大小和形状。 一般来说,这两个行动遵循的规则如下:
膨胀:输出像素的值是落在结构元素大小和形状内的所有像素的最大值。 例如,在二进制图像中,如果输入图像中落在内核范围内的任何像素被设置为值1,则输出图像的对应像素也将被设置为1。 后者适用于任何类型的图像(例如灰度,bgr等)。
侵蚀:反之亦然适用于侵蚀操作。 输出像素的值是落在结构化元素的大小和形状内的所有像素的最小值。 看看下面的例子:
如上所述以及通常在任何形态学操作中所看到的,用于探测输入图像的结构化元素是最重要的部分。
结构化元素是由0和1组成的矩阵,可以有任意的形状和大小。 通常比正在处理的图像小得多,而值为1的像素定义邻域。 结构化元素的中心像素,称为原点,标识感兴趣的像素 - 正在处理的像素。
例如,以下示出了7×7尺寸的菱形结构元件。
结构化元素可以具有许多常见形状,例如线条,菱形,圆盘,周期线以及圆和大小。 您通常会选择与要在输入图像中处理/提取的对象大小和形状相同的结构元素。 例如,要查找图像中的线条,请创建一个线性结构化元素,稍后将会看到。
本教程代码的显示如下。 您也可以从这里下载。
#include
#include
using namespace std;
using namespace cv;
int main(int, char** argv)
{
// Load the image
Mat src = imread(argv[1]);
// Check if image is loaded fine
if(!src.data)
cerr << "Problem loading image!!!" << endl;
// Show source image
imshow("src", src);
// Transform source image to gray if it is not
Mat gray;
if (src.channels() == 3)
{
cvtColor(src, gray, CV_BGR2GRAY);
}
else
{
gray = src;
}
// Show gray image
imshow("gray", gray);
// Apply adaptiveThreshold at the bitwise_not of gray, notice the ~ symbol
Mat bw;
adaptiveThreshold(~gray, bw, 255, CV_ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 15, -2);
// Show binary image
imshow("binary", bw);
// Create the images that will use to extract the horizontal and vertical lines
Mat horizontal = bw.clone();
Mat vertical = bw.clone();
// Specify size on horizontal axis
int horizontalsize = horizontal.cols / 30;
// Create structure element for extracting horizontal lines through morphology operations
Mat horizontalStructure = getStructuringElement(MORPH_RECT, Size(horizontalsize,1));
// Apply morphology operations
erode(horizontal, horizontal, horizontalStructure, Point(-1, -1));
dilate(horizontal, horizontal, horizontalStructure, Point(-1, -1));
// Show extracted horizontal lines
imshow("horizontal", horizontal);
// Specify size on vertical axis
int verticalsize = vertical.rows / 30;
// Create structure element for extracting vertical lines through morphology operations
Mat verticalStructure = getStructuringElement(MORPH_RECT, Size( 1,verticalsize));
// Apply morphology operations
erode(vertical, vertical, verticalStructure, Point(-1, -1));
dilate(vertical, vertical, verticalStructure, Point(-1, -1));
// Show extracted vertical lines
imshow("vertical", vertical);
// Inverse vertical image
bitwise_not(vertical, vertical);
imshow("vertical_bit", vertical);
// Extract edges and smooth image according to the logic
// 1. extract edges
// 2. dilate(edges)
// 3. src.copyTo(smooth)
// 4. blur smooth img
// 5. smooth.copyTo(src, edges)
// Step 1
Mat edges;
adaptiveThreshold(vertical, edges, 255, CV_ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 3, -2);
imshow("edges", edges);
// Step 2
Mat kernel = Mat::ones(2, 2, CV_8UC1);
dilate(edges, edges, kernel);
imshow("dilate", edges);
// Step 3
Mat smooth;
vertical.copyTo(smooth);
// Step 4
blur(smooth, smooth, Size(2, 2));
// Step 5
smooth.copyTo(vertical, edges);
// Show final result
imshow("smooth", vertical);
waitKey(0);
return 0;
}
1、加载源图像并检查是否没有任何问题,然后显示它:
// Load the image
Mat src = imread(argv[1]);
// Check if image is loaded fine
if(!src.data)
cerr << "Problem loading image!!!" << endl;
// Show source image
imshow("src", src);
// Transform source image to gray if it is not
Mat gray;
if (src.channels() == 3)
{
cvtColor(src, gray, CV_BGR2GRAY);
}
else
{
gray = src;
}
// Show gray image
imshow("gray", gray);
// Apply adaptiveThreshold at the bitwise_not of gray, notice the ~ symbol
Mat bw;
adaptiveThreshold(~gray, bw, 255, CV_ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 15, -2);
// Show binary image
imshow("binary", bw);
4、现在我们准备应用形态学操作来提取水平线和垂直线,从而将音乐表与音乐片断分开,但是首先让我们初始化我们将使用的输出图像:
// Create the images that will use to extract the horizontal and vertical lines
Mat horizontal = bw.clone();
Mat vertical = bw.clone();
5、正如我们在理论中指定的那样,为了提取我们想要的对象,我们需要创建相应的结构元素。 由于这里我们想要提取水平线,为此目的的相应结构元素将具有以下形状:
并在源代码中由以下代码片段表示:
// Specify size on horizontal axis
int horizontalsize = horizontal.cols / 30;
// Create structure element for extracting horizontal lines through morphology operations
Mat horizontalStructure = getStructuringElement(MORPH_RECT, Size(horizontalsize,1));
// Apply morphology operations
erode(horizontal, horizontal, horizontalStructure, Point(-1, -1));
dilate(horizontal, horizontal, horizontalStructure, Point(-1, -1));
// Show extracted horizontal lines
imshow("horizontal", horizontal);
6、垂直线也是如此,具有相应的结构元素:
并再次表示如下:
// Specify size on vertical axis
int verticalsize = vertical.rows / 30;
// Create structure element for extracting vertical lines through morphology operations
Mat verticalStructure = getStructuringElement(MORPH_RECT, Size( 1,verticalsize));
// Apply morphology operations
erode(vertical, vertical, verticalStructure, Point(-1, -1));
dilate(vertical, vertical, verticalStructure, Point(-1, -1));
// Show extracted vertical lines
imshow("vertical", vertical);
7、正如你所看到的,我们快到了。 但是,在这一点上你会注意到音符的边缘有些粗糙。 出于这个原因,我们需要改进边缘以获得更平滑的结果:
// Inverse vertical image
bitwise_not(vertical, vertical);
imshow("vertical_bit", vertical);
// Extract edges and smooth image according to the logic
// 1. extract edges
// 2. dilate(edges)
// 3. src.copyTo(smooth)
// 4. blur smooth img
// 5. smooth.copyTo(src, edges)
// Step 1
Mat edges;
adaptiveThreshold(vertical, edges, 255, CV_ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 3, -2);
imshow("edges", edges);
// Step 2
Mat kernel = Mat::ones(2, 2, CV_8UC1);
dilate(edges, edges, kernel);
imshow("dilate", edges);
// Step 3
Mat smooth;
vertical.copyTo(smooth);
// Step 4
blur(smooth, smooth, Size(2, 2));
// Step 5
smooth.copyTo(vertical, edges);
// Show final result
imshow("smooth", vertical);
使用OpenCV函数cv :: pyrUp和cv :: pyrDown下采样或上采样给定的图像。
要生成高斯金字塔中的图层 (i+1) ,我们执行以下操作:
用高斯内核卷积 Gi :
删除偶数行和列。
当我们缩小图像的大小时,实际上是丢失了图像的信息。 |
#include "opencv2/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
using namespace cv;
Mat src, dst, tmp;
const char* window_name = "Pyramids Demo";
int main( void )
{
printf( "\n Zoom In-Out demo \n " );
printf( "------------------ \n" );
printf( " * [u] -> Zoom in \n" );
printf( " * [d] -> Zoom out \n" );
printf( " * [ESC] -> Close program \n \n" );
src = imread( "../data/chicky_512.png" ); // Loads the test image
if( src.empty() )
{ printf(" No data! -- Exiting the program \n");
return -1; }
tmp = src;
dst = tmp;
imshow( window_name, dst );
for(;;)
{
char c = (char)waitKey(0);
if( c == 27 )
{ break; }
if( c == 'u' )
{ pyrUp( tmp, dst, Size( tmp.cols*2, tmp.rows*2 ) );
printf( "** Zoom In: Image x 2 \n" );
}
else if( c == 'd' )
{ pyrDown( tmp, dst, Size( tmp.cols/2, tmp.rows/2 ) );
printf( "** Zoom Out: Image / 2 \n" );
}
imshow( window_name, dst );
tmp = dst;
}
return 0;
}
我们来看看程序的一般结构:
src = imread( "../data/chicky_512.png" ); // Loads the test image
if( src.empty() )
{ printf(" No data! -- Exiting the program \n");
return -1; }
Mat src, dst, tmp;
/* ... */
tmp = src;
dst = tmp;
imshow( window_name, dst );
for(;;)
{
char c = (char)waitKey(0);
if( c == 27 )
{ break; }
if( c == 'u' )
{ pyrUp( tmp, dst, Size( tmp.cols*2, tmp.rows*2 ) );
printf( "** Zoom In: Image x 2 \n" );
}
else if( c == 'd' )
{ pyrDown( tmp, dst, Size( tmp.cols/2, tmp.rows/2 ) );
printf( "** Zoom Out: Image / 2 \n" );
}
imshow( window_name, dst );
tmp = dst;
}
我们的程序退出,如果用户按ESC键。 另外还有两个选择:
if( c == 'u' )
{ pyrUp( tmp, dst, Size( tmp.cols*2, tmp.rows*2 ) );
printf( "** Zoom In: Image x 2 \n" );
}
我们使用函数cv :: pyrUp有三个参数:
else if( c == 'd' )
{ pyrDown( tmp, dst, Size( tmp.cols/2, tmp.rows/2 ) );
printf( "** Zoom Out: Image / 2 \n" );
}
与cv :: pyrUp类似,我们使用函数cv :: pyrDown,它有三个参数:
tmp = dst;
使用OpenCV函数cv :: threshold执行基本的阈值操作
#include "opencv2/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
using namespace cv;
int threshold_value = 0;
int threshold_type = 3;
int const max_value = 255;
int const max_type = 4;
int const max_BINARY_value = 255;
Mat src, src_gray, dst;
const char* window_name = "Threshold Demo";
const char* trackbar_type = "Type: \n 0: Binary \n 1: Binary Inverted \n 2: Truncate \n 3: To Zero \n 4: To Zero Inverted";
const char* trackbar_value = "Value";
void Threshold_Demo( int, void* );
int main( int, char** argv )
{
src = imread( argv[1], IMREAD_COLOR ); // Load an image
if( src.empty() )
{ return -1; }
cvtColor( src, src_gray, COLOR_BGR2GRAY ); // Convert the image to Gray
namedWindow( window_name, WINDOW_AUTOSIZE ); // Create a window to display results
createTrackbar( trackbar_type,
window_name, &threshold_type,
max_type, Threshold_Demo ); // Create Trackbar to choose type of Threshold
createTrackbar( trackbar_value,
window_name, &threshold_value,
max_value, Threshold_Demo ); // Create Trackbar to choose Threshold value
Threshold_Demo( 0, 0 ); // Call the function to initialize
for(;;)
{
char c = (char)waitKey( 20 );
if( c == 27 )
{ break; }
}
}
void Threshold_Demo( int, void* )
{
/* 0: Binary
1: Binary Inverted
2: Threshold Truncated
3: Threshold to Zero
4: Threshold to Zero Inverted
*/
threshold( src_gray, dst, threshold_value, max_BINARY_value,threshold_type );
imshow( window_name, dst );
}
1、我们来看看程序的一般结构:
src = imread( argv[1], IMREAD_COLOR ); // Load an image
if( src.empty() )
{ return -1; }
cvtColor( src, src_gray, COLOR_BGR2GRAY ); // Convert the image to Gray
namedWindow( window_name, WINDOW_AUTOSIZE ); // Create a window to display results
createTrackbar( trackbar_type,
window_name, &threshold_type,
max_type, Threshold_Demo ); // Create Trackbar to choose type of Threshold
createTrackbar( trackbar_value,
window_name, &threshold_value,
max_value, Threshold_Demo ); // Create Trackbar to choose Threshold value
void Threshold_Demo( int, void* )
{
/* 0: Binary
1: Binary Inverted
2: Threshold Truncated
3: Threshold to Zero
4: Threshold to Zero Inverted
*/
threshold( src_gray, dst, threshold_value, max_BINARY_value,threshold_type );
imshow( window_name, dst );
}
正如你所看到的,函数cv :: threshold被调用。 我们给5个参数: