opencv之图像操作

 

1、图像操作

①读取图像
②像素操作

Vec3b对应三通道的顺序是blue、green、red的uchar类型数据

所涉及函数:
cvtColor():函数是一个颜色空间转换函数,可以实现RGB颜色向HSV,HSI等颜色空间转换。也可以转换为灰度图。
bitwise_not(src,dst):是对二进制数据进行“非”操作,即对图像(灰度图像或彩色图像均可)每个像素值进行二进制“非”操作,~1=0,~0=1。

#include
#include
#include

using namespace std;
using namespace cv;

int main(int argc, char *argv[])
{
    Mat src = imread("1.jpg");
    if(src.empty()){
        cout<<"could not load image"<(row,col);
            gray_src.at(row,col) = 255 - gray;
        }
    }
    imshow("invertImage",gray_src);

    Mat dst;
    dst.create(src.size(),src.type());
    width = src.cols;
    height = src.rows;
    int channel = src.channels();
    //等同于bitwise_not(src,dst);
    for(int row = 0;row < height;row++)
    {
        for(int col = 0; col < width;col++)
        {
            int b = src.at(row,col)[0];
            int g = src.at(row,col)[1];
            int r = src.at(row,col)[2];
            dst.at(row,col)[0] = 255 -b;
            dst.at(row,col)[1] = 255 -g;
            dst.at(row,col)[2] = 255 -r;
        }
    }
    imshow("invertImageBGR",dst);
    
    waitKey(0);
    return 0;
}

2、图像混合

①线性混合操作:

g(x)=(1-\alpha)f_0(x)+\alpha f_1(x)
相关API:
addWeighted()
 

#include
#include
#include

using namespace std;
using namespace cv;

int main(int argc, char *argv[])
{
    
    Mat src1,src2,dst;
    src1 = imread("mogu.jpg");
    src2 = imread("rain.jpg");
    if(src1.empty()||src2.empty())
    {
        cout<<"could not load image"<

3、调整图像对比度与亮度

图像变换可以分为:①像素变换--点操作②邻域操作--区域。调整图像亮度与对比度属于点操作。

g(i,j)=af(i,j)+b 

参数a:增益,控制图像的对比度;
参数b:偏置,控制图像亮度。


//-----------------------------------【头文件包含部分】---------------------------------------
//	描述:包含程序所依赖的头文件
//---------------------------------------------------------------------------------------------- 
#include 
#include 
#include "opencv2/imgproc/imgproc.hpp"
//#include 
#include 

//-----------------------------------【命名空间声明部分】---------------------------------------
//	描述:包含程序所使用的命名空间
//-----------------------------------------------------------------------------------------------   
using namespace std;
using namespace cv;


//-----------------------------------【全局函数声明部分】--------------------------------------
//	描述:全局函数声明
//-----------------------------------------------------------------------------------------------
static void ContrastAndBright(int, void *);


//-----------------------------------【全局变量声明部分】--------------------------------------
//	描述:全局变量声明
//-----------------------------------------------------------------------------------------------
int g_nContrastValue; //对比度值
int g_nBrightValue;  //亮度值
Mat g_srcImage,g_dstImage;
//-----------------------------------【main( )函数】--------------------------------------------
//	描述:控制台应用程序的入口函数,我们的程序从这里开始
//-----------------------------------------------------------------------------------------------
int main(   )
{
	//改变控制台前景色和背景色
	//system("color 2F");  

	// 读入用户提供的图像
	g_srcImage = imread( "1.jpg");
	if( !g_srcImage.data ) { printf("读取g_srcImage图片错误~! \n"); return false; }
	g_dstImage = Mat::zeros( g_srcImage.size(), g_srcImage.type() );

	//设定对比度和亮度的初值
	g_nContrastValue=80;
	g_nBrightValue=80;

	//创建窗口
	namedWindow("rendering_window",1);

	//创建轨迹条
	createTrackbar("contrast:", "rendering_window",&g_nContrastValue, 300,ContrastAndBright );
	createTrackbar("brightness:", "rendering_window",&g_nBrightValue, 200,ContrastAndBright );

	//调用回调函数
	ContrastAndBright(g_nContrastValue,0);
	ContrastAndBright(g_nBrightValue,0);

	//输出一些帮助信息
	cout<(y,x)[c] = saturate_cast( (g_nContrastValue*0.01)*( g_srcImage.at(y,x)[c] ) + g_nBrightValue );
			}
		}
	}

	// 显示图像
	//namedWindow("【原始图窗口】");
	imshow("original_window", g_srcImage);
	//namedWindow("【效果图窗口】");
	imshow("rendering_window", g_dstImage);
}

4、图像模糊

图像模糊是图像处理中最简单和常用的操作之一,其主要目的之一是给图像预处理的时候降低图像噪声。比如,在大目标提取之前去除图像中的一些琐碎细节。图像的模糊通常依靠图像的卷积操作来实现。图像模糊又被称为平滑滤波。

卷积相关知识参考:https://www.zhihu.com/question/22298352

常见的图像模糊方法如下:

(1) 归一化均值滤波器(API为blur()
(2) 高斯滤波器(API为GaussianBlur()
(3) 中值滤波器(API为medianBlur()):对椒盐噪声有很好的滤除效果
(4) 双边滤波器(API为bilateralFilter()):可有效保留图像边沿特征

相关API介绍即原理知识参考:https://www.cnblogs.com/laizhenghong2012/p/11278888.html

5、形态学操作

5.1腐蚀与膨胀

简单来讲,形态学操作就是基于形状的一系列图像处理操作。通过将 结构元素 作用于输入图像来产生输出图像。两种最基本的形态学操作,腐蚀与膨胀( Erosion 与 Dilation),他们的运用广泛:①消除噪声;②分割(isolate)独立的图像元素,以及连接(join)相邻的元素;③寻找图像中的明显的极大值区域或极小值区域。通过以下图像,我们简要来讨论一下膨胀与腐蚀操作:

opencv之图像操作_第1张图片

膨胀:

  • 此操作将图像 A 与任意形状的内核 (B),通常为正方形或圆形,进行卷积。

  • 内核 B 有一个可定义的 锚点, 通常定义为内核中心点。

  • 进行膨胀操作时,将内核 B 划过图像,将内核 B 覆盖区域的最大相素值提取,并代替锚点位置的相素。显然,这一最大化操作将会导致图像中的亮区开始”扩展” (因此有了术语膨胀 dilation)。对上图采用膨胀操作我们得到:

opencv之图像操作_第2张图片

腐蚀:

  • 腐蚀在形态学操作家族里是膨胀操作的孪生姐妹。它提取的是内核覆盖下的相素最小值。

  • 进行腐蚀操作时,将内核 B 划过图像,将内核 B 覆盖区域的最小相素值提取,并代替锚点位置的相素。

  • 以与膨胀相同的图像作为样本,我们使用腐蚀操作。从下面的结果图我们看到亮区(背景)变细,而黑色区域(字母)则变大了。

opencv之图像操作_第3张图片

#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
//#include "highgui.h"

using namespace cv;

/// 全局变量
Mat src, erosion_dst, dilation_dst;

int erosion_size = 0;
int dilation_size = 0;
int const max_kernel_size = 21;

/** Function Headers */
void Erosion( int, void* );
void Dilation( int, void* );

/** @function main */
int main( int argc, char** argv )
{
  /// Load 图像
  src = imread( "1.jpg" );

  if( !src.data )
  { return -1; }

  /// 创建显示窗口
  namedWindow( "Erosion Demo", CV_WINDOW_AUTOSIZE );
  namedWindow( "Dilation Demo", CV_WINDOW_AUTOSIZE );
  cvMoveWindow( "Dilation Demo", src.cols*2, 0 );

  /// 创建腐蚀 Trackbar
  createTrackbar( "Kernel size:\n 2n +1", "Erosion Demo",
                  &erosion_size, max_kernel_size,
                  Erosion );

  /// 创建膨胀 Trackbar
  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=MORPH_RECT;//MORPH_RECT,MORPH_CROSS,MORPH_ELLIPSE
  Mat element = getStructuringElement( erosion_type,
                                       Size( 2*erosion_size + 1, 2*erosion_size+1 ),
                                       Point( -1, -1 ) );

  /// 腐蚀操作
  erode( src, erosion_dst, element );
  imshow( "Erosion Demo", erosion_dst );
}

/** @function Dilation */
void Dilation( int, void* )
{
  int dilation_type=MORPH_RECT;//MORPH_RECT,MORPH_CROSS,MORPH_ELLIPSE
  Mat element = getStructuringElement( dilation_type,
                                       Size( 2*dilation_size + 1, 2*dilation_size+1 ),
                                       Point( -1, -1 ) );
  ///膨胀操作
  dilate( src, dilation_dst, element );
  imshow( "Dilation Demo", dilation_dst );
}

5.2 开操作、闭操作

① 开操作- open   open() = dilate(erode())
    先腐蚀后膨胀,可以去掉小的对象,假设对象是前景色,背景是黑色。作用:用来消除图像中细小对象,在纤细点处分离物体和平滑较大物体的边界而有不明显改变其面积和形状,所有小到不能容纳结构元素的物体都会被移除。
② 闭操作- close  close() = erode(dilate())
    先膨胀后腐蚀(bin2),可以填充前景上小的洞(fill hole),假设对象是前景色,背景是黑色。作用:用来填充目标内部的细小孔洞(fill hole),将断开的邻近目标连接,在不明显改变物体面积和形状的情况下平滑其边界,基本上所有小到不能完整容纳结构元素的空隙或间隙,都会被闭运算消除(即连起来)。

int main()
{
    //首先读入图像,并二值化
    cv::Mat srcImage = cv::imread("../pictures/000177.png",cv::IMREAD_GRAYSCALE);
    cv::threshold(srcImage,srcImage,125,255,cv::THRESH_BINARY);
 
    cv::imshow("srcImage",srcImage);
 
 
    //获取进行形态学操作的核
    cv::Mat elementRect;
    elementRect = cv::getStructuringElement(cv::MORPH_RECT,cv::Size(3,3),cv::Point(-1,-1));
 
    //开运算
    cv::Mat opendImage;
    cv::morphologyEx(srcImage,opendImage,cv::MORPH_OPEN,elementRect);
    cv::cvtColor(opendImage,opendImage,cv::COLOR_GRAY2BGR);
    cv::putText(opendImage,"opendImage",cv::Point(0,30),cv::FONT_HERSHEY_SIMPLEX,1.2,cv::Scalar(0,255,0),2);
 
    //闭运算
    cv::Mat closedImage;
    cv::morphologyEx(srcImage,closedImage,cv::MORPH_CLOSE,elementRect);
    cv::cvtColor(closedImage,closedImage,cv::COLOR_GRAY2BGR);
    cv::putText(closedImage,"closedImage",cv::Point(0,30),cv::FONT_HERSHEY_SIMPLEX,1.2,cv::Scalar(0,255,0),2);
 
    cv::hconcat(opendImage,closedImage,opendImage);
    cv::imshow("res",opendImage);
 
 
    cv::waitKey(0);
    return 0;
}

5.3 形态学操作

形态学梯度- Morphological Gradient   dst = dilate() - erode()    可以让像素间的像素值形成梯度。

梯度用于刻画目标边界或边缘位于图像灰度级剧烈变化的区域,形态学梯度根据膨胀或者腐蚀与原图作差组合来实现增强结构元素领域中像素的强度,突出高亮区域的外围。计算图像的形态学梯度是形态学重要操作,常常将膨胀和腐蚀基础操作组合起来一起使用实现一些复杂的图像形态学梯度。可以计算的梯度常见如下四种:

①基本梯度:是用膨胀后的图像减去腐蚀后的图像得到差值图像,称为梯度图像也是OpenCV中支持的计算形态学②梯度的方法,而此方法得到梯度有被称为基本梯度。
③内部梯度:是用原图像减去腐蚀之后的图像得到差值图像,称为图像的内部梯度。
④外部梯度:是用图像膨胀之后再减去原来的图像得到的差值图像,称为图像的外部梯度。
⑤方向梯度:是使用X方向与Y方向的直线作为结构元素之后得到图像梯度,用X方向直线做结构元素分别膨胀与腐蚀之后得到图像求差值之后称为X方向梯度,用Y方向直线做结构元素分别膨胀与腐蚀之后得到图像求差值之后称为Y方向梯度。

形态学梯度操作的输出图像像素值是在对应结构元素而非局部过渡区域所定义的领域中灰度级强度变化的最大值。对二值图像进行形态学操作可以将团块(blob)的边缘突出出来,可以用形态学梯度来保留物体的边缘轮廓。

int main()
{
//首先读入图像,并二值化
    cv::Mat srcImage = cv::imread("../pictures/000177.png",cv::IMREAD_GRAYSCALE);
    cv::threshold(srcImage,srcImage,125,255,cv::THRESH_BINARY);
 
    cv::imshow("srcImage",srcImage);
 
    //获取进行形态学操作的核
    cv::Mat elementRect;
    elementRect = cv::getStructuringElement(cv::MORPH_RECT,cv::Size(3,3),cv::Point(-1,-1));
 
    //膨胀
    cv::Mat dilateImage;
    cv::dilate(srcImage,dilateImage,elementRect);
    //cv::morphologyEx(srcImage,dilateImage,cv::MORPH_DILATE,elementRect);
 
    //腐蚀
    cv::Mat erodeImage;
    cv::erode(srcImage,erodeImage,elementRect);
    //cv::morphologyEx(srcImage,dilateImage,cv::MORPH_ERODE,elementRect);
 
 
    //1.计算基本梯度:膨胀后的图像减去腐蚀后的图像
    cv::Mat basicGradient;
    cv::subtract(dilateImage, erodeImage, basicGradient, cv::Mat());
    //cv::morphologyEx(srcImage,basicGradient,cv::MORPH_GRADIENT,elementRect);//两者等效
    cv::imshow("basicGradient", basicGradient);
 
    //2.计算内部梯度:原图像减去腐蚀之后的图像
    cv::Mat internalGradientImg;
    cv::subtract(srcImage, erodeImage, internalGradientImg, cv::Mat());
    cv::imshow("internalGradientImg", internalGradientImg);
 
    //3.计算外部梯度:膨胀后的图像减去原图像
    cv::Mat externalGradientImg;
    cv::subtract(dilateImage, srcImage, externalGradientImg, cv::Mat());
    cv::imshow("externalGradientImg", externalGradientImg);
 
    //4.方向梯度:使用X方向与Y方向的直线作为结构元素---------
    //cv::Mat hse = getStructuringElement(cv::MORPH_RECT, cv::Size(srcImage.cols / 16, 1),cv::Point(-1,-1));
    //cv::Mat vse = getStructuringElement(cv::MORPH_RECT, cv::Size(1, srcImage.rows / 16),cv::Point(-1,-1));
    cv::Mat hse = getStructuringElement(cv::MORPH_RECT, cv::Size(3, 1),cv::Point(-1,-1));
    cv::Mat vse = getStructuringElement(cv::MORPH_RECT, cv::Size(1, 3),cv::Point(-1,-1));
    cv::Mat  xDirectImg, yDirectImg;
    //4.1 X 方向梯度:膨胀与腐蚀之后得到图像求差值
    cv::erode(srcImage, erodeImage, hse);
    cv::dilate(srcImage, dilateImage, hse);
    cv::subtract(dilateImage, erodeImage, xDirectImg, cv::Mat());
    cv::imshow("xDirectImg", xDirectImg);
    //cv::imshow("dilateImage",dilateImage);
    //cv::imshow("erodeImage",erodeImage);
    //4.2 Y 方向梯度:膨胀与腐蚀之后得到图像求差值
    cv::erode(srcImage, erodeImage, vse);
    cv::dilate(srcImage, dilateImage, vse);
    cv::subtract(dilateImage, erodeImage, yDirectImg, cv::Mat());
    cv::imshow("yDirectImg", yDirectImg);
 
    cv::waitKey(0);
    return 0;
}

5.4 顶帽、黑帽

①顶帽 – top hat
    顶帽 是原图像与开操作之间的差值图像,结果类似于 !开操作 的图像结果。因为开运算带来的结果是放大了裂缝或者局部低亮度的区域,因此,从原图中减去开运算后的图,得到的效果图突出了比原图轮廓周围的区域更明亮的区域,且这一操作和选择的核的大小相关。因此,顶帽变换用于凸显暗背景上的亮物体。对二值图来说,进行顶帽变换或之后底帽变换看起来就像是加一个阴影,有一种立体的效果。顶帽运算往往用来分离比邻近点亮一些的斑块。当一幅图像具有大幅的背景的时候,而微小物品比较有规律的情况下,可以使用顶帽运算进行背景提取。
②黑帽 – black hat
    黑帽是闭操作图像与源图像的差值图像,可以检测出原图前景色中的黑点。黑帽运算后的效果图突出了比原图轮廓周围的区域更暗的区域,且这一操作和选择的核的大小相关。所以,黑帽运算用来分离比邻近点暗一些的斑块。黑帽变换可以用于凸显亮背景上的暗物体。二值图效果与顶帽变换相比,就是一个方向相反的阴影。

int main()
{
    //首先读入图像
    cv::Mat srcImage = cv::imread("../pictures/000177.png",cv::IMREAD_GRAYSCALE);
    //cv::threshold(srcImage,srcImage,125,255,cv::THRESH_BINARY);
 
    cv::imshow("srcImage",srcImage);
 
    //获取进行形态学操作的核
    cv::Mat elementRect;
    elementRect = cv::getStructuringElement(cv::MORPH_RECT,cv::Size(3,3),cv::Point(-1,-1));
 
    //顶帽、黑帽
    cv::Mat topHatImage;
    cv::morphologyEx(srcImage,topHatImage,cv::MORPH_TOPHAT,elementRect,cv::Point(-1,-1));
    cv::imshow("topHatImage",topHatImage);
 
 
    cv::Mat blackHatImage;
    cv::morphologyEx(srcImage,blackHatImage,cv::MORPH_TOPHAT,elementRect,cv::Point(-1,-1));
    cv::imshow("blackHatImage",blackHatImage);
 
    cv::waitKey(0);
    return 0;
}

5.5 应用:提取水平线与垂直线

提取步骤:
①输入彩色图像;
②转化为灰度图cvtColor;
③转换为二值图像adaptiveThreshold;
④定义结构元素;
⑤开操作(腐蚀+膨胀)提取水平与垂直线。

# include 
# include 
 
 
using namespace cv;
int main(int argc, char* argv) {
	Mat src, dst;
	src = imread("1.png");
	if (src.empty()) {
		printf("Could not load image...\n");
		return -1;
	}
	char INPUT_WIN[] = "input image";
	char OUTPUT_WIN[] = "result image";
	namedWindow(INPUT_WIN, CV_WINDOW_AUTOSIZE);
	imshow(INPUT_WIN, src);
	// 变成灰度图像
	Mat gray_src;
	cvtColor(src, gray_src, CV_BGR2GRAY);
	imshow("gray_src", gray_src);
 
	//变成二值图像
	Mat binImg;
	adaptiveThreshold(~gray_src, binImg, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 15, -2);
	imshow("binary image", binImg);
 
	//水平结构元素
	Mat hline = getStructuringElement(MORPH_RECT, Size(src.cols / 16, 1), Point(-1, -1));
	//垂直结构元素
	Mat vline = getStructuringElement(MORPH_RECT, Size(1, src.rows / 16), Point(-1, -1));
	
 
	// 形态学开操作——先腐蚀后膨胀
	Mat temp;
	//erode(binImg, temp, hline);
	//dilate(temp, dst, hline);
	morphologyEx(binImg, dst, CV_MOP_OPEN, hline);
	bitwise_not(dst, dst);
	//blur(dst, dst, Size(3, 3), Point(-1, -1));
	imshow("Final Result", dst);
	imwrite("C:/Users/12914/Pictures/char1_change.png", dst);
	waitKey(0);
	return 0;
}

相关API:

void adaptiveThreshold(InputArray src, OutputArray dst,  
                       double maxValue, int adaptiveMethod,  
                       int thresholdType, int bolckSize, double C)

参数4:指定自适应阈值算法。可选择ADAPTIVE_THRESH_MEAN_C 或 ADAPTIVE_THRESH_GAUSSIAN_C两种。(具体见下面的解释)。
参数5:指定阈值类型。可选择THRESH_BINARY或者THRESH_BINARY_INV两种。(即二进制阈值或反二进制阈值)。
参数6:表示邻域块大小,用来计算区域阈值,一般选择为3、5、7......等。
参数7:参数C表示与算法有关的参数,它是一个从均值或加权均值提取的常数,可以是负数。(具体见下面的解释)。

自适应阈值化计算大概过程是为每一个象素点单独计算的阈值,即每个像素点的阈值都是不同的,就是将该像素点周围B*B区域内的像素加权平均,然后减去一个常数C,从而得到该点的阈值。B由参数6指定,常数C由参数7指定。

ADAPTIVE_THRESH_MEAN_C,为局部邻域块的平均值,该算法是先求出块中的均值,再减去常数C。

ADAPTIVE_THRESH_GAUSSIAN_C,为局部邻域块的高斯加权和。该算法是在区域中(x, y)周围的像素根据高斯函数按照他们离中心点的距离进行加权计算,再减去常数C。

6、图像金字塔-上采样-下采样

在图像处理中常常会调整图像大小,最常见的就是放大和缩小,可通过图像金字塔的方法实现。实现对输入图像的上采样和下采样操作,使用到pyrUP和pyrDown两个函数来对分别对图像进行上采样和下采样。

pyrUp(InputArray src, OutputArray dst, const Size& dstsize=Size());
参数三:目标图像大小。默认情况下,它是计算尺寸((SRC.列+ 1)/ 2、(SRC.行+ 1)/ 2)。

pyrDown(InputArraysrc, OutputArraydst, const Size&dstsize=Size());
参数三:目标图像大小。默认情况下,它是计算尺寸((SRC.列+ 1)/ 2、(SRC.行+ 1)/ 2)。

两种常见的图像金子塔如下所述:
1.高斯金字塔:用于下采样图像。
2.拉普拉斯金字塔:用于把下层低分辨率的图像进行上采样重建。

6.1 高斯金字塔

①对图像进行高斯滤波(对图像进行加权平均,每个像素点由其本身和邻域内的其他像素值经过加权平均后得到,即用高斯核与原图像进行卷积)。
②删除所有的偶数行和列。

6.2 拉普拉斯金字塔

用来从金字塔低层图像重建上层未采样图像,在数字图像处理中也即是预测残差,可以对图像进行最大程度的还原,配合高斯金字塔一起使用。

详细参考:https://www.cnblogs.com/sddai/p/10330756.html

include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include 
#include 
#include 

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" );

  //测试图像必须是2^(n)
  src = imread("D:\\lena.bmp");
  if( !src.data )
    { printf(" No data! -- Exiting the program \n");
      return -1; }

  tmp = src;
  dst = tmp;//交换图像信息

  //创建输出显示窗口
  namedWindow( window_name, CV_WINDOW_AUTOSIZE );
  imshow( window_name, dst );

  //循环操作
  for(;;)
  {
    int c;
    c = waitKey(10);

    if( (char)c == 27 )//按下键盘的"ESC"退出程序
      { break; }


    if( (char)c == 'u' )//按下键盘的"u"进行上采样操作
      { 
		  //tmp:原图像 ,dst:输出图像,是原图像的两倍,
		  //Size:dst图像的大小,行和列扩大2倍
		  pyrUp( tmp, dst, Size( tmp.cols*2, tmp.rows*2 ) );
          printf( "** Zoom In: Image x 2 \n" );
      }

    else if( (char)c == 'd' )//按下键盘的"d"进行下采样操作
      { 
		  pyrDown( tmp, dst, Size( tmp.cols/2, tmp.rows/2 ) );
		  printf( "** Zoom Out: Image / 2 \n" );
      }
    
	//创建输出显示窗口
    imshow( window_name, dst );
    tmp = dst;//更新temp值为了更好的递归操作
   }
   return 0;
}

 

你可能感兴趣的:(opencv之图像操作)