opencv-基础篇

CSDN文章下载链接:

https://download.csdn.net/download/qq_38649386/13986488

 

目录

1  图像采集 1

1.1  数据类型 1

1.1.1  Mat 1

1.2  图像加载、截取、转色域 3

1.2.1  格式与信息 3

1.2.2  imread 3

1.2.3  Rect 4

1.2.4  cvtColor 5

1.3  加载视频 6

1.3.1  VideoCapture 1 读摄像头 6

1.3.2  VideoCapture 2 拍照保存 6

1.3.3  VideoCapture 3 保存视频 7

1.3.4  VideoCapture 4 读取视频 8

1.4  图像掩膜 9

1.4.1  像素指针 9

1.4.2  像素范围处理 9

1.4.3  filter2D & src.ptr<>() 9

1.4.4  src.at<>() 11

2  几何变换 14

2.1  形状变换 14

2.1.1  图像缩放 14

2.1.2  resize 14

2.1.3  pyramid特征金字塔 16

2.1.4  DOG 高斯不同 17

2.1.2  图像错切 18

2.2  位置变换 19

2.2.1  平移变换 19

2.2.2  镜像变换 19

2.2.3  旋转变换 20

2.2.4  warpAffine 20

2.3  仿射变换 22

2.4  基本运算 22

2.4.1  点运算 22

2.4.2  point process像素访问 23

2.4.3  代数运算 25

2.4.4  addWeighted 25

2.4.5  逻辑运算 26

2.4.6  &|~ 27

2.4.7  split 29

2.4.8  plant画图形 30

3  图像增强 33

3.1  对比度线性展宽 33

3.1.1  change contrast 33

3.2  非线性动态范围调整 34

3.3  直方图均衡化 35

3.4  伪彩色增强 35

3.4.1  密度分层法 35

3.4.2  空域灰度级彩色变换法 36

3.4.3  频域伪彩色增强法 36

3.4.4  applyColorMap 36

3.4.5  LUT 36

4  图像去噪 38

4.1  常见噪声模型与滤波模型 38

4.1.1  addSaltNoise 38

4.2  主要滤波函数 39

4.2.1  均值滤波 39

4.2.2  中值滤波 39

4.2.3  高斯滤波 39

4.2.4  双边滤波 40

4.3  filter API 40

4.3.1  filter 1 40

4.3.2  filter 2 41

4.3.3  filter 3 42

4.3.4  filter 4 43

4.4  其他滤波方法 44

5  边缘提取 45

5.1  Robert算子 45

5.1.1  All Filter Function 45

5.2  Sobel算子 47

5.2.1  example code 1 48

5.2.2  sobel&scharr提取边界 48

5.3  Priwitt算子 51

5.3  Laplance算子 51

5.3.1  example code 1 52

5.3.2  example code 2 52

5.3.3  example code 3 53

5.4  Canny算子 54

5.4.1  example code 1 54

5.4.2  example code 2 55

5.5  边缘处理 57

5.5.1  copyMakeBorder 57

5.5.2  自适应阈值分割 58

6  图像形态学 60

6.1  膨胀 60

6.2  腐蚀 60

6.2.1  dilate&erode 1 60

6.2.2  dilate&erode 2 61

6.3  开运算 63

6.4  闭运算 63

6.4.1  open&close 1 64

6.5  形态学梯度 64

6.6  顶帽运算 65

6.7  黑帽运算 65

6.7.1  open&close&top&black 65

6.8  提取水平和垂直线 67

6.8.1  example code 67

 

 

 

1  图像采集

1.1  数据类型

Mat(nrow

,ncols

,type

[,fillValue]

)

数据类型:

U usigned char(无符号字符型,不是无符号整型)

C 通道数

S signed int(有符号整型)

CV_8U 0~255

CV_8S 0~255

CV_32S int型的范围

CV_32F 浮点型的范围

填充的数值(矩阵fillvalue):

Scalar(0, 0, 255) 红色,自动填充三通道颜色

Scalar(100) 灰度图颜色填充

Size(240,250) 创建Mat大小

1.1.1  Mat

/**********************************

@函数名称:create_image

@函数输入:

@函数输出:

@函数功能:用Mat创建图片

 ,自定义格式

 ,颜色

**********************************/

void create_image() {

// cv::Mat img(240, 240, CV_8UC3, cv::Scalar(0, 0, 255));

cv::Mat img(cv::Size(240, 500), CV_8UC3, cv::Scalar(0, 0, 255));

 

cv::imshow("test", img);

cv::waitKey(0);

cv::destroyAllWindows();

}

 

/**********************************

@函数名称:Mat_image

@函数输入:none

@函数输出:none

@函数功能:图像Mat操作

**********************************/

void Mat_image(void) {

/* 原图像 */

cv::Mat src = cv::imread("./img/shi.png", cv::IMREAD_COLOR);

if (!src.data) { // src.empty()

std::cerr << "error input" << std::endl;

return;

}

cv::namedWindow("srcImage", CV_WINDOW_AUTOSIZE);

cv::imshow("srcImage", src);

 

/* method Mat */

cv::Mat dst1;

dst1 = cv::Mat(src.size(), src.type(), cv::Scalar(0,0,255)); // 自定义初始化数据

// dst1 = cv::Scalar(0,0,255);

cv::namedWindow("dst1Image", CV_WINDOW_AUTOSIZE);

cv::imshow("dst1Image", dst1);

 

cv::Mat M(5, 5, CV_8UC3, cv::Scalar(127));

std::cout << "M =" << std::endl << M << std::endl;

 

/* method copyTo */

cv::Mat dst2;

src.copyTo(dst2); // API

cv::namedWindow("dst2Image", CV_WINDOW_AUTOSIZE);

cv::imshow("dst2Image", dst2);

 

/* method clone */

cv::Mat dst3 = src.clone(); // 完全数据拷贝

cv::namedWindow("dst3Image", CV_WINDOW_AUTOSIZE);

cv::imshow("dst3Image", dst3);

 

/* method create */

cv::Mat dst4;

dst4.create(src.size(), src.type());

dst4 = cv::Scalar(0, 255, 255);

cv::namedWindow("dst4Image", CV_WINDOW_AUTOSIZE);

cv::imshow("dst4Image", dst4);

 

/* 图像指针 */

cv::Mat graydst;

cv::cvtColor(src, graydst, cv::COLOR_BGR2GRAY);

const uchar* firstRow = graydst.ptr<uchar>(0);

// std::cout << "gray image's first point is: " << *firstRow << std::endl; //没有结果

printf("fist pixel value : %d\n", *firstRow);

 

/* 尺寸 */

int row = graydst.rows;

int col = graydst.cols;

int channel = graydst.channels();

std::cout << "The image's size is: " << row << " * " << col << " * " << channel << std::endl;

 

cv::waitKey(0);

cv::destroyAllWindows();

}

1.2  图像加载、截取、转色域

1.2.1  格式与信息

flags包括:

IMREAD_GRAYSCALE 灰色

IMREAD_COLOR 彩色

image information:

image.empty() 判断是否为空,空位true

image.cols 列数

image.rows 行数

image.channels() 通道数

1.2.2  imread

imread(filename

,int flags=IMREAD_COLOR

)

/**********************************

@函数名称:read_image

@函数输入:图片目录地址

@函数输出:显示图片窗口

@函数功能:读取图片

**********************************/

void read_image() {

std::string img_addr = "./img/shi.jpg"; // 设置读取目录地址

 

/* 输入读取图像方式 */

cv::Mat image = cv::imread(img_addr); // 读取图片

// cv::Mat image = cv::imread(img_addr, cv::IMREAD_GRAYSCALE); // 灰色图像-CV_8U

// cv::Mat image = cv::imread(img_addr, cv::IMREAD_COLOR); // 彩色图像-CV_8UC3

 

if (image.empty()) {  // 错误处理

std::cout << "input image error!!!" << std::endl;

return;

}

/* 显示加载图片基本信息*/

std::cout << "This image size is " << image.rows << " * " << image.cols << std::endl;

std::cout << "This image channal has " << image.channels() << "channels" << std::endl;

 

cv::namedWindow("InputImage"); // 创建窗口

cv::imshow("InputImage", image); // 显示图片

cv::waitKey(0); // 等待按键输入

cv::destroyAllWindows(); // 关闭所有窗口

}

1.2.3  Rect

region of interest。ROI-感兴趣区域。Rect Rect(int x, int y, int width, ind height)

// 列,行,加列,加行

/**********************************

@函数名称:ROI-》Rect函数

@函数输入:

@函数输出:

@函数功能:选取感兴趣区域,并叠加

**********************************/

void ROI_image(void) {

/* 加载图片2 */

cv::Mat img2 = cv::imread("./img/shi.png");

cv::namedWindow("image2");

cv::imshow("image2", img2);

std::cout << "image 1:" << img2.rows << " * " << img2.cols << std::endl;

 

cv::Mat imageROI = img2(cv::Rect(170, 10, 100, 120));

//  列, 行,加列,加行

cv::namedWindow("ROIimage");

cv::imshow("ROIimage", imageROI);

 

cv::waitKey(0);

cv::destroyAllWindows();

}

1.2.4  cvtColor

void cv::cvtColor (InputArray src,

OutputArray dst,

int code,

int dstCn = 0

)

/**********************************

@函数名称:cvtcolor_image

@函数输入:none

@函数输出:none

@函数功能:转色域

**********************************/

void read_image_V(void) {

/* 原图像 */

cv::Mat src = cv::imread("./img/shi.png", cv::IMREAD_COLOR);

if (!src.data) { // src.empty()

std::cerr << "error input" << std::endl;

return;

}

 

/* 转色域 */

cv::Mat dst;

cv::cvtColor(src, dst,cv::COLOR_BGR2GRAY);

 

cv::namedWindow("srcImage", CV_WINDOW_NORMAL);

cv::namedWindow("dstImage", CV_WINDOW_AUTOSIZE);

 

cv::imshow("srcImage", src);

cv::imshow("dstImage", dst);

 

cv::waitKey(0);

cv::destroyAllWindows();

}

1.3  加载视频

保存格式:(opencv版本不一样,宏定义不一样)

cv::CAP_OPENCV_MJPEG MP4

 

1.3.1  VideoCapture 1 读摄像头

/**********************************

@函数名称:read_camera

@函数输入:

@函数输出:

@函数功能:调用摄像头

**********************************/

void read_camera(void) {

cv::VideoCapture myCapture(0); // 摄像头型号

cv::namedWindow("ShowCamera", cv::WINDOW_NORMAL); // 窗口设置,可手动调整

while (1) {

cv::Mat myFrame;

myCapture >> myFrame;

 

if (myFrame.empty()) {

break;

}

cv::imshow("ShowCamera", myFrame);

cv::waitKey(10);

}

}

1.3.2  VideoCapture 2 拍照保存

/**********************************

@函数名称:take_pictures

@函数输入:可修改调用摄像头序号

           可修改图片保存目录地址

@函数输出:

@函数功能:调用摄像头,按space拍照

按ESC退出

**********************************/

void take_pictures() {

cv::VideoCapture mycapture(0);

std::string writePath = "./img/";

std::string name;

cv::Mat frame;

int i = 0;

 

while (true){

mycapture >> frame; // 从视频序列中取出一张图片数据,存储到frame

//if (!mycapture.isOpened()) // 有360摄像头保护容易出错

// std::cout << "error" << std::endl;

// break;

if (frame.empty()) // 输入保护

break;

if (cv::waitKey(1) == 32) { // space拍照保存

std::cout << "saving picture" << std::endl;

//name = writePath + char(i) + ".jpg";

name = writePath + char('0'+i) + ".jpg";

cv::imwrite(name, frame);

std::cout << name << std::endl;

i++;

}

cv::imshow("readCamera", frame);

if (cv::waitKey(100) == 27) // ESC退出

break; // 1-10效果不好,100还行,稍有卡顿

}

}

1.3.3  VideoCapture 3 保存视频

/**********************************

@函数名称:write_video

@函数输入:

@函数输出:

@函数功能:保存摄像头视频,按ESC退出

**********************************/

void write_video() {

cv::VideoCapture myCapture(0); // 初始化摄像头

cv::VideoWriter myVideoWritter; // 生成视频实例化

 

myVideoWritter.open(

"./img/Video1.mp4" // avi 不行

, cv::CAP_OPENCV_MJPEG // 和格式有关

, 30.0

, cv::Size(640, 480)

, true

);

 

cv::namedWindow("show", cv::WINDOW_NORMAL);

int i = 0;

while (i < 100) {

i++;

cv::Mat myFrame;

myCapture >> myFrame;

 

if (myFrame.empty()) {

break;

}

cv::imshow("show", myFrame);

//myVideoWriter << myFrame;

myVideoWritter.write(myFrame);

cv::waitKey(30);

}

myCapture.release();

myVideoWritter.release();

 

}

 

 

 

1.3.4  VideoCapture 4 读取视频

/**********************************

@函数名称:read_video

@函数输入:

@函数输出:

@函数功能:读取视频

**********************************/

void read_video(void) {

cv::VideoCapture myCapture("./img/Video1.mp4"); // 摄像头型号

cv::namedWindow("ShowCamera", cv::WINDOW_NORMAL); // 窗口设置,可手动调整

while (1) {

cv::Mat myFrame;

myCapture >> myFrame;

 

if (myFrame.empty()) {

break;

}

cv::imshow("ShowCamera", myFrame);

cv::waitKey(10);

}

}

1.4  图像掩膜

1.4.1  像素指针

Mat.ptr(int i=0)

// 获取像素矩阵的指针,索引i表示第几,从0开始计行数。

const uchar*  current= myImage.ptr(row );

// 获得当前行指针

1.4.2  像素范围处理

像素范围处理saturate_cast

saturate_cast(-100),返回 0。

saturate_cast(288),返回255

saturate_cast(100),返回100

这个函数的功能是确保RGB值得范围在0~255之间

 

1.4.3  filter2D & src.ptr<>()

void cv::filter2D (InputArray src,

OutputArray dst,

int ddepth,

InputArray kernel,

Point anchor = Point(-1,-1),

double delta = 0,

int borderType = BORDER_DEFAULT

)

/**********************************

@函数名称:ptr_image

@函数输入:none

@函数输出:none

@函数功能:图像像素操作

**********************************/

void ptr_image(void) {

cv::Mat src, dst_ptr, dst_API;

/* 加载原图像 */

src = cv::imread("./img/shiyuan.png", cv::IMREAD_COLOR);

if (!src.data) { // src.empty()

std::cerr << "could not load image..." << std::endl;

return;

}

 

/* 指针像素点处理 */

int rows = src.rows; // 行

int cols = (src.cols - 1) * src.channels(); // 真实列数为列数*通道数,并且需要减一

int offsetx = src.channels(); // 通道

 

dst_ptr = cv::Mat::zeros(src.size(), src.type()); // 初始化dst

// 两个需要时函数,变量报错

/* 定义耗费时间 */

double time, timeCost;

time = cv::getTickCount();

/* 遍历行 */

for (int row = 1; row < rows - 1; row++) { // 留出第一行,留出最后一行,自行处理

/* 初始化指针变量 */

const uchar* previous = src.ptr<uchar>(row - 1); // 指向前一行

const uchar* current = src.ptr<uchar>(row); // 指向当前行

const uchar* next = src.ptr<uchar>(row + 1); // 指向后一行

uchar* output = dst_ptr.ptr<uchar>(row); // output已经指向当前行

 

/* 遍历列 */

for (int col = offsetx; col < cols; col++) { // 在行一定的条件下,遍历列

output[col]=cv::saturate_cast<uchar>( // output在当前行中,计算各个列的值

5 * current[col] - (current[col - offsetx]

+ current[col + offsetx]

+ previous[col] + next[col]

)

);

}

}

timeCost = (cv::getTickCount() - time) / cv::getTickFrequency();

std::cout << "ptr method cost time: " << timeCost << std::endl;

 

/* API-filter2D像素处理 */

time = cv::getTickCount();

cv::Mat kernel = (cv::Mat_<char>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0); // 是char,不是uchar

cv::filter2D(src, dst_API, src.depth(), kernel); // -1

timeCost = (cv::getTickCount() - time) / cv::getTickFrequency();

std::cout << "API method time cost: " << timeCost << std::endl;

 

/* 创建窗口 */

cv::namedWindow("srcImage", CV_WINDOW_AUTOSIZE);

cv::namedWindow("dstImage_ptr", CV_WINDOW_AUTOSIZE);

cv::namedWindow("dstImage_API", CV_WINDOW_AUTOSIZE);

 

/* 显示图片 */

cv::imshow("srcImage", src);

cv::imshow("dstImage_ptr", dst_ptr);

cv::imshow("dstImage_API", dst_ptr);

 

/* 销毁窗口 */

cv::waitKey(0);

cv::destroyAllWindows();

}

1.4.4  src.at<>()

/**********************************

@函数名称:point_image

@函数输入:none

@函数输出:none

@函数功能:图像负片效果操作

**********************************/

void point_image(void) {

/* 加载原图像 */

cv::Mat src;

src = cv::imread("./img/shiyuan.png");

if (!src.data) {

std::cerr << "Could not load image" << std::endl;

return;

}

cv::namedWindow("srcImage", CV_WINDOW_AUTOSIZE);

cv::imshow("srcImage", src);

 

/* 转灰色图像 */

cv::Mat gray_src;

cv::cvtColor(src, gray_src, CV_BGR2GRAY);

cv::namedWindow("grayImage", CV_WINDOW_AUTOSIZE);

cv::imshow("grayImage", gray_src);

 

/* 单通道处理 */

/* 函数操作像素,非指针操作 */

cv::Mat gray_dst;

gray_dst.create(gray_src.size(), gray_src.type()); // 必须要初始化

int height = gray_src.rows;

int width = gray_src.cols;

 

for (int row = 0; row < height; row++) {  

for (int col = 0; col < width; col++) {

int gray = gray_src.at<char>(row, col); // 类函数操作像素

gray_dst.at<uchar>(row, col) = 255 - gray; // 底片效果

}

}

cv::namedWindow("grayImage_at", CV_WINDOW_AUTOSIZE);

cv::imshow("grayImage_at", gray_dst);

 

/* 多通道处理 */

cv::Mat dst;

dst.create(src.size(), src.type());

// dst = cv::Scalar(255, 0, 0); // 初始化颜色-蓝色

height = src.rows;

width = src.cols;

int nc = src.channels();

 

for (int row = 0; row < height; row++) {

for (int col = 0; col < width; col++) {

if (nc == 1) {

int gray = gray_src.at<char>(row, col); // 用的灰色原图数据

dst.at<uchar>(row, col) = 255 - gray;

}

else if(nc==3){

int b = src.atVec3b>(row, col)[0]; // 这里保存的是原图的数值

int g = src.atVec3b>(row, col)[1];

int r = src.atVec3b>(row, col)[2];

dst.atVec3b>(row, col)[0] = 255 - b; // 根据BGR原图数值处理后,保存在结果dst中

dst.atVec3b>(row, col)[1] = 255 - g;

dst.atVec3b>(row, col)[2] = 255 - r;

}

 

}

}

cv::namedWindow("Image_channels", CV_WINDOW_AUTOSIZE);

cv::imshow("Image_channels", dst);

 

/* bitwise_not API */

cv::Mat dst_bitwise;

cv::bitwise_not(src, dst_bitwise);

cv::namedWindow("Image_bitwise", CV_WINDOW_AUTOSIZE);

cv::imshow("Image_bitwise", dst_bitwise);

 

/* 取最值的方法处理成灰色图 */

cv::Mat dst_maxmin;

dst_maxmin.create(src.size(), src.type());

height = src.rows;

width = src.cols;

nc = src.channels();

 

for (int row = 0; row < height; row++) {

for (int col = 0; col < width; col++) {

if (nc == 1) {

int gray = gray_src.at<char>(row, col);

dst_maxmin.at<uchar>(row, col) = 255 - gray;

}

else if (nc == 3) {

int b = src.atVec3b>(row, col)[0]; // 这里保存的是原图的数值

int g = src.atVec3b>(row, col)[1];

int r = src.atVec3b>(row, col)[2];

gray_dst.at<uchar>(row, col) = min(r, min(g, b)); // max & min

}

}

}

cv::namedWindow("Image_maxmin", CV_WINDOW_AUTOSIZE);

cv::imshow("Image_maxmin", gray_dst);

 

cv::waitKey(0);

cv::destroyAllWindows();

}

 

 

2  几何变换

Geometric Operation

2.1  形状变换

2.1.1  图像缩放

 

即:

 

fx,fy为缩放比例。

2.1.2  resize

Resizes an image。

C++:

void resize(InputArray src

, OutputArray dst

, Size dsize

, double fx=0

, double fy=0

, int interpolation=INTER_LINEAR

)

Python:

cv2.resize(src, dsize[, dst[, fx[, fy[, interpolation]]]]) → dst

Parameters:

 

 

插值方法:

INTER_NEAREST-最近邻插值

INTER_LINEAR-双线性插值(默认情况下使用)

区域间-使用像素-面积关系重新采样。这可能是图像抽取的首选方法,因为它可以得到无云纹的结果。但当图像被放大时,它类似于最近邻法。

INTER_CUBIC-4x4像素邻域上的双三次插值

INTER_LANCZOS4-8x8像素邻域上的Lanczos插值

/**********************************

@函数名称:interpolation

@函数输入:

@函数输出:

@函数功能:resize

**********************************/

void interpolation(void) {

cv::Mat srcImage = cv::imread("./img/shi.png");

cv::Mat dstImage;

 

cv::imshow("srcShow", srcImage);

cv::resize(srcImage, dstImage, cv::Size(256, 256), 0, 0, cv::INTER_LINEAR);

 

cv::imshow("desShow", dstImage);

cv::waitKey(0);

}

2.1.3  pyramid特征金字塔

pyrUp:

Upsamples an image and then blurs it.

C++:

void pyrUp(InputArray src

, OutputArray dst

, const Size& dstsize=Size()

, int borderType=BORDER_DEFAULT

)

Python:

cv2.pyrUp(src[, dst[, dstsize[, borderType]]]) → dst

C:

cvPyrUp(const CvArr* src, CvArr* dst, int filter=CV_GAUSSIAN_5x5 )

 

pyrDown

Blurs an image and downsamples it.

C++:

void pyrDown(InputArray src

, OutputArray dst

, const Size& dstsize=Size()

, int borderType=BORDER_DEFAULT

)

Python:

cv2.pyrDown(src[, dst[, dstsize[, borderType]]]) → dst

C:

void cvPyrDown(const CvArr* src, CvArr* dst, int filter=CV_GAUSSIAN_5x5 )

 

/**********************************

@函数名称:pyramid

@函数输入:

@函数输出:

@函数功能:金字塔法放缩图像

**********************************/

void pyramid(void) {

/* 原始图像 */

cv::Mat srcImage = cv::imread("./img/shi.png");

cv::Mat dstImage0, dstImage1;

cv::imshow("Source Image", srcImage);

/* 上采样 */

cv::pyrUp(srcImage, dstImage0, cv::Size(srcImage.cols * 2, srcImage.rows * 2));

cv::imshow("pyrUp Image", dstImage0);

/* 下采样 */

cv::pyrDown(srcImage, dstImage1, cv::Size(srcImage.cols / 2, srcImage.rows / 2));

cv::imshow("pyrDown Image", dstImage1);

 

cv::waitKey(0);

cv::destroyAllWindows();

}

2.1.4  DOG 高斯不同

/**********************************

@函数名称:pyr_up_dowm

@函数输入:none

@函数输出:none

@函数功能:采样-DOG

**********************************/

void pyr_up_down(void) {

/* 原图像 */

cv::Mat src = cv::imread("./img/shiyuan.png");

if (!src.data) {

std::cerr << "error input" << std::endl;

return;

}

cv::namedWindow("srcImage", CV_WINDOW_AUTOSIZE);

cv::imshow("srcImage", src);

 

/* 上采样 */

cv::Mat dst_pyrup;

cv::pyrUp(src, dst_pyrup, cv::Size(src.cols * 2, src.rows * 2)); // 先列,再行

cv::imshow("pyrup", dst_pyrup);

 

/* 下采样 */

cv::Mat dst_pyrdown;

cv::pyrDown(src, dst_pyrdown, cv::Size(src.cols / 2, src.rows / 2)); // 先列,再行

cv::imshow("pyrdown", dst_pyrdown);

 

/* DOG-高斯不同 */

cv::Mat dst_gray, g1, g2, dst_dog;

cv::cvtColor(src, dst_gray, CV_BGR2GRAY);

cv::GaussianBlur(dst_gray, g1, cv::Size(5, 5), 0, 0);

cv::GaussianBlur(g1, g2, cv::Size(3, 3), 0, 0);

cv::subtract(g1, g2, dst_dog, cv::Mat()); // 低减高

 

/* 归一化显示 */

cv::normalize(dst_dog, dst_dog, 255, 0, cv::NORM_MINMAX);

cv::imshow("DOG Image", dst_dog);

 

cv::waitKey(0);

cv::destroyAllWindows();

}

2.1.2  图像错切

设变换矩阵T。

经过水平错切后:

 

c为y坐标轴与图像边界夹角c=tanα,当c<0,则沿+x方向错切,反之,c>0,则沿-x方向错切。

经过垂直错切后:

 

b为x坐标轴与图像边界夹角b=tanβ,当b>0,则沿+y方向错切,反之,b<0,则沿-y方向错切。

2.2  位置变换

2.2.1  平移变换

 

得:

 

2.2.2  镜像变换

1.水平镜像变换:

(M行N列)X轴坐标不变的情况下,Y轴坐的标数值变。

 

2.垂直镜像变换:

(M行N列)Y轴坐标不变的情况下,X轴坐标的数值变。

 

3.对角镜像变换:

(M行N列)Y轴坐标的数值变,X轴坐标的数值变。

 

2.2.3  旋转变换

旋转前:(旋转之前,直线与X轴夹角为β)

 

 

旋转后:(旋转之后,两条直线之间的夹角为α)

 

 

矩阵形式:

 

2.2.4  warpAffine

Applies an affine transformation to an image.

C++:

void warpAffine(InputArray src

, OutputArray dst

, InputArray M

, Size dsize

, int flags=INTER_LINEAR

, int borderMode=BORDER_CONSTANT

, const Scalar& borderValue=Scalar()

)

Python:

cv2.warpAffine(src, M, dsize[, dst[, flags[, borderMode[, borderValue]]]]) → dst

 

/**********************************

@函数名称:rotate_image

@函数输入:

@函数输出:

@函数功能:图像旋转

**********************************/

void rotate_image(void) {

int degree = 45; // 定义旋转角度

 

/* 原始图像 */

cv::Mat srcImage = cv::imread("./img/shi.png");

cv::Mat dstImage;

cv::imshow("srcImage", srcImage);

 

/* 旋转中心为图像中心 */

cv::Point2f center;  

center.x = float(srcImage.cols / 2.0 + 0.5);

center.y = float(srcImage.rows / 2.0 + 0.5);

 

/* 计算二维旋转的仿射变换矩阵 */

cv::Mat M; // 仿射变换矩阵

M = cv::getRotationMatrix2D(center, degree, 0.7);

 

/* 变换图像,并用黑色填充 */

cv::warpAffine(srcImage, dstImage, M, dstImage.size());

cv::imshow("dstshow", dstImage);

 

cv::waitKey(0);

cv::destroyAllWindows();

}

2.3  仿射变换

 

,表示恒等变换

,表示平移变换

,表示缩放变换

,表示旋转变换

2.4  基本运算

2.4.1  点运算

Point Operation-点运算也称为:对比度增强、对比度拉伸、灰度变换

T表示灰度变换函数,g()表示输出图像灰度,f()表示输入图像灰度。

 

1.线性点运算:

 

当a>1,输出图像灰度拓展,对比度增大;

当0

当a=1,b=0,输出图像的灰度不变,对比度不变;

当a<0,输出图像中的暗区域变亮,亮区域变暗;

2.分段线性点运算:

分段线性点运算,主要是将图像中感兴趣区域的灰度范围进行线性拓展,同时抑制不感兴趣的灰度区域。

假设原图像灰度范围为[0,Mf],变换后灰度变换范围为[0,Mg]:

 

 

3.非线性点运算:

对数变换:

 

幂次变换:

 

当0<γ<1,加亮、减暗;

当γ>1,加暗、减亮;

2.4.2  point process像素访问

/**********************************

@函数名称:point_process

@函数输入:

@函数输出:

@函数功能:像素访问

**********************************/

void point_process(void) {

/* 原始图像初始化 */

cv::Mat image = cv::imread("./img/shi.png");

cv::imshow("srcImageShow", image);

 

/* 指针操作 */

double start = static_cast<double>(cv::getTickCount());

int rowNumber = image.rows;

// 每一行元素个数 = 列数 * 通道数

int colNumber = image.cols * image.channels();

for (int i = 0; i < rowNumber; i++) { // 行循环

uchar* data = image.ptr<uchar>(i); // 获取第i行的首地址(默认第一个元素)

// ()-行元素;()[]列元素

for (int j = 0; j < colNumber; j++) {

/* 开始处理 */

data[j] = 255;

}

}

double end = static_cast<double>(cv::getTickCount());

double time = (end - start) / cv::getTickFrequency();

std::cout << "The method 1 runs: " << time << "seconds" << std::endl;

cv::imshow("Method 1", image);

 

/* 迭代器操作 */

start = static_cast<double>(cv::getTickCount());

cv::Mat_Vec3b>::iterator it = image.beginVec3b>(); // 初始位置的迭代器

cv::Mat_Vec3b>::iterator itend = image.endVec3b>(); // 终止位置的迭代器

for (; it != itend; it++) {

// 处理BGR三通道

(*it)[0] = 255; // B

(*it)[1] = 255; // G

(*it)[2] = 0; // R

}

end = static_cast<double>(cv::getTickCount());

time = (end - start) / cv::getTickFrequency();

std::cout << "The method 2 runs: " << time << "seconds" << std::endl;

cv::imshow("Method 2", image);

 

/* 动态地址计算 */

start = static_cast<double>(cv::getTickCount());

rowNumber = image.rows;

colNumber = image.cols;

for (int i = 0; i < rowNumber; i++) {

for (int j = 0; j < colNumber; j++) {

// 处理BGR通道数据

image.atVec3b>(i, j)[0] = 0;

image.atVec3b>(i, j)[1] = 255;

image.atVec3b>(i, j)[2] = 0;

}

}

end = static_cast<double>(cv::getTickCount());

time = (end - start) / cv::getTickFrequency();

std::cout << "The method 3 runs: " << time << "seconds" << std::endl;

cv::imshow("Method 3", image);

cv::waitKey(0);

cv::destroyAllWindows();

}

2.4.3  代数运算

Algebra Operation

 

2.4.4  addWeighted

/**********************************

@函数名称:add_image

@函数输入:

@函数输出:

@函数功能:图像叠加

**********************************/

void add_image(void) {

/* 初始化图像 */

cv::Mat srcImage0 = cv::imread("./img/shi.png");

cv::resize(srcImage0, srcImage0, cv::Size(500, 500), 0, 0, cv::INTER_LINEAR);

cv::namedWindow("show0");

cv::imshow("show0", srcImage0);

 

cv::Mat srcImage1 = cv::imread("./img/Yui.png");

cv::resize(srcImage1, srcImage1, cv::Size(500, 500), 0, 0, cv::INTER_LINEAR);

cv::namedWindow("show1");

cv::imshow("show1", srcImage1);

 

/* 叠加图像 */

cv::Mat dstImage;

cv::addWeighted(srcImage0, 0.5, srcImage1, 0.5, 0, dstImage, -1);

cv::namedWindow("show2");

cv::imshow("show2", dstImage);

 

cv::waitKey(0);

cv::destroyAllWindows();

}

/**********************************

@函数名称:add_image

@函数输入:none

@函数输出:none

@函数功能:图像融合

**********************************/

void add_image_V(void) {

/* 读取图像 */

cv::Mat src1 = cv::imread("./img/shiyuan.png");

cv::Mat src2 = cv::imread("./img/shi.png");

if (!src1.data) {

std::cout << "could not load the first image..." << std::endl;

return;

}

if (!src2.data) {

std::cout << "could not load the scond image ..." << std::endl;

return;

}

/* resize */

cv::resize(src1, src1, cv::Size(500, 500));

cv::resize(src2, src2, cv::Size(500, 500));

 

cv::imshow("src1", src1);

cv::imshow("src2", src2);

 

/* 图像加运算 */

double alpha = 0.5;

cv::Mat dst;

if (src1.rows == src2.rows && src1.cols == src2.cols && src1.type() == src2.type()) {

// 确保尺寸一致

cv::addWeighted(src1, alpha, src2, 1 - alpha, 0.0, dst);

cv::imshow("dst", dst);

}

else {

std::cout << "The size of two images are wrong ..." << std::endl;

cv::destroyAllWindows();

return;

}

 

cv::waitKey(0);

cv::destroyAllWindows();

}

2.4.5  逻辑运算

Logical Operation

反运算:(R为最大灰度值)

 

与运算:

 

或运算:

 

异或运算:

 

2.4.6  &|~

C++:

void circle(InputOutputArray img

, Point center

, int radius

, const Scalar& color

, int thickness=1

, int lineType=LINE_8

, int shift=0

)

Python:

cv2.circle(img, center, radius, color[, thickness[, lineType[, shift]]]) → img

 

C++:

void rectangle(Mat& img

, Rect rec

, const Scalar& color

, int thickness=1

, int lineType=LINE_8

, int shift=0

)

 

/**********************************

@函数名称:logical_process

@函数输入:

@函数输出:

@函数功能:图像逻辑运算

**********************************/

void logical_process(void) {

// 生成256*256三通道零值图像

cv::Mat imageCircle = cv::Mat::zeros(256, 256, CV_8UC3);

cv::Mat imageRect = cv::Mat::zeros(256, 256, CV_8UC3);

// 绘制实心圆和实心矩阵

cv::circle(imageCircle, cv::Point(127, 127), 100, cv::Scalar(255, 255, 255), -1, 8, 0);

cv::rectangle(imageRect,cv::Rect(37, 37, 180, 180),cv::Scalar(255, 255, 255),-1,8,0);

 

cv::namedWindow("imageCircle");

cv::imshow("imageCircle", imageCircle);

cv::namedWindow("imageRect");

cv::imshow("imageRect", imageRect);

 

cv::Mat tempImage0 = cv::Mat::zeros(256, 256, CV_8UC3);

cv::Mat tempImage1 = cv::Mat::zeros(256, 256, CV_8UC3);

cv::Mat tempImage2 = cv::Mat::zeros(256, 256, CV_8UC3);

 

tempImage0 = imageCircle & imageRect;

tempImage1 = imageCircle | imageRect;

tempImage2 = ~imageCircle;

 

cv::namedWindow("Image-AND");

cv::imshow("Image-AND", tempImage0);

cv::namedWindow("Image-OR");

cv::imshow("Image-OR", tempImage1);

cv::namedWindow("Image-NOT");

cv::imshow("Image-NOT", tempImage2);

 

cv::waitKey(0);

cv::destroyAllWindows();

}

2.4.7  split

C++:

void split(InputArray m

, OutputArrayOfArrays mv

)

 

/**********************************

@函数名称:RGB_process

@函数输入:

@函数输出:

@函数功能:图像多通道处理

**********************************/

void RGB_process(void) {

cv::Mat srcImage = cv::imread("./img/shi.png");

cv::namedWindow("show");

cv::imshow("show", srcImage);

 

cv::Mat dstImage;

std::vectorMat> srcrgbChannels(3);

cv::split(srcImage, srcrgbChannels);

 

cv::namedWindow("channel 0");

cv::imshow("channel 0", srcrgbChannels[0]);

cv::namedWindow("channel 1");

cv::imshow("channel 1", srcrgbChannels[1]);

cv::namedWindow("channel 2");

cv::imshow("channel 2", srcrgbChannels[2]);

 

cv::waitKey(0);

cv::destroyAllWindows();

}

2.4.8  plant画图形

/**********************************

@函数名称:plant_image

@函数输入:none

@函数输出:none

@函数功能:画图

**********************************/

void plant_image(void) {

/* 原图 */

cv::Mat bgImage = cv::imread("./img/Yui.png");

if (!bgImage.data) {

printf("could not load image...\n");

return;

}

std::cout << bgImage.size << std::endl;

/* 画线 */

cv::Point p1 = cv::Point(20, 30);

cv::Point p2;

p2.x = 200;

p2.y = 200;

cv::Scalar color = cv::Scalar(0, 0, 255);

cv::line(bgImage, p1, p2, color, 5, cv::LINE_8); // 5:线粗细,LINE_8:线类型

 

/* 方框 */

cv::Rect rect = cv::Rect(200, 200, 300, 500);

//   列,行,加列,加行

color = cv::Scalar(255, 0, 0);

cv::rectangle(bgImage, rect, color, 2, cv::LINE_8);

 

/* 椭圆 */

color = cv::Scalar(0, 255, 0);

cv::ellipse(bgImage

, cv::Point(bgImage.cols / 2, bgImage.rows / 2)

, cv::Size(bgImage.cols / 4, bgImage.rows / 8)

, 90, 0, 360, color, 7, cv::LINE_8

); // 背景图,中心点,半径,角度,起始角度,颜色,粗细,线类型

 

/* 圆 */

color = cv::Scalar(0, 255, 255);

cv::Point center = cv::Point(bgImage.cols / 2, bgImage.rows / 2);

cv::circle(bgImage, center, 150, color, 2, 8);

// 背景,中心,半径,颜色,粗细,类型

 

/* 填充 */

cv::Point pts[1][5]; // 四个角,五个点,相连为矩形

pts[0][0] = cv::Point(100, 100);

pts[0][1] = cv::Point(100, 200);

pts[0][2] = cv::Point(200, 200);

pts[0][3] = cv::Point(200, 100);

pts[0][4] = cv::Point(100, 100);

 

const cv::Point* ppts[] = { pts[0] };

int npt[] = { 5 };

color = cv::Scalar(255, 0, 255);

cv::fillPoly(bgImage, ppts, npt, 1, color, 8);

// 背景,多个多边形顶点的数组的列表,顶点数

 

/* 文字 */

cv::putText(bgImage

, "Hello OpenCV!!!"

, cv::Point(300,300) // 起始位置

, CV_FONT_HERSHEY_COMPLEX // 字体

, 1.0 // 字体大小

, cv::Scalar(12, 23, 200)

, 3, 8 // 字体粗细

);

 

/* 随机画图 */

cv::RNG rng(12345);

cv::Point pt1;

cv::Point pt2;

cv::Mat bg = cv::Mat::zeros(bgImage.size(), bgImage.type());

cv::namedWindow("random line demo", CV_WINDOW_AUTOSIZE);

 

for (int i = 0; i < 1000; i++) {

pt1.x = rng.uniform(0, bgImage.cols);

pt2.x = rng.uniform(0, bgImage.cols);

pt1.y = rng.uniform(0, bgImage.rows);

pt2.y = rng.uniform(0, bgImage.rows);

 

color=cv::Scalar(rng.uniform(0, 255)

, rng.uniform(0, 255)

, rng.uniform(0, 255)

);

if (cv::waitKey(50) > 0) {

break;

}

cv::line(bg, pt1, pt2, color, 2, 8);

cv::imshow("random line demo", bg);

}

 

/* 显示最后效果图 */

char drawdemo_win[] = "Result Image";

cv::namedWindow(drawdemo_win, CV_WINDOW_AUTOSIZE);

cv::imshow(drawdemo_win, bgImage);

cv::waitKey(0);

cv::destroyAllWindows();

}

 

3  图像增强

图像增强不是增强图像的数据,而是增加图像中感兴趣区域的动态特征范围。

3.1  对比度线性展宽

对比度指图像中的明暗层次等级;

展宽是指利用分段函数,降低不重要信息的对比度,增加重要信息的对比度。

作用:

展宽后,图像对比度增强,图像中细节信息更容易辨识。

 

3.1.1  change contrast

/**********************************

@函数名称:change_contrast

@函数输入:none

@函数输出:none

@函数功能:调图像对比度

**********************************/

void change_contrast(void) {

cv::Mat src = cv::imread("./img/shiyuan.png");

if (!src.data) {

std::cout << "input error..." << std::endl;

return;

}

// cv::cvtColor(src, src, CV_BGR2GRAY); // 用于测试灰度图

char inputTittle[] = "srcImage";

cv::imshow(inputTittle, src);

 

std::cout << src.type() << std::endl;

 

/* 多通道处理 */

int height = src.rows;

int width = src.cols;

int channel = src.channels();

float alpha = 1.1;

float beta = 30;

cv::Mat dst = cv::Mat::zeros(src.size(), src.type()); // 初始化全零图像

// src.convertTo(dst, CV_32F);

std::cout << src.type() << std::endl;

 

for (int row = 0; row < height; row++) {

for (int col = 0; col < width; col++) {

if (channel == 1) {

int value = src.at<uchar>(row, col);

dst.at<uchar>(row, col) = cv::saturate_cast<uchar>(value * alpha + beta);

}

else if (channel == 3) {

int b = src.atVec3b>(row, col)[0];

int g = src.atVec3b>(row, col)[1];

int r = src.atVec3b>(row, col)[2];

 

dst.atVec3b>(row, col)[0] = cv::saturate_cast<uchar>(alpha * b + beta);

dst.atVec3b>(row, col)[1] = cv::saturate_cast<uchar>(alpha * g + beta);

dst.atVec3b>(row, col)[2] = cv::saturate_cast<uchar>(alpha * r + beta);

}

else {

std::cout << "error size" << std::endl;

}

}

}

char outputTittle[] = "dstImage";

cv::imshow(outputTittle, dst);

 

cv::waitKey(0);

cv::destroyAllWindows();

}

3.2  非线性动态范围调整

公式:

 

作用:

抑制高亮度区域,拓展低亮度区域,恰好解决高亮度区域信号掩盖低亮度区域信号的问题。

 

3.3  直方图均衡化

概念:

通过对原图进行某种变换,使原图像的灰度直方图修正为均匀分布的直方图。

 

/**********************************

@函数名称:balance_image

@函数输入:

@函数输出:

@函数功能:彩色图直方图均衡化

**********************************/

void balance_image(void) {

cv::Mat image = cv::imread("./img/shi.png");

cv::imshow("srcImage", image);

 

cv::Mat imageRGB[3];

cv::split(image, imageRGB); // RGB通道分离

for (int i = 0; i < 3; i++) { // 单个通道直方图均衡化

cv::equalizeHist(imageRGB[i], imageRGB[i]);

}

cv::merge(imageRGB, 3, image);

cv::imshow("直方图效果", image);

 

cv::waitKey(0);

cv::destroyAllWindows();

}

3.4  伪彩色增强

3.4.1  密度分层法

将灰度图像进行一定数量的分级,并进行颜色的映射。

方法:

1.图像像素逐个映射;

2.多光谱图像任意三个光谱图像映射为RGB图像;

3.黑白图像灰度级映射和频谱映射;

3.4.2  空域灰度级彩色变换法

利用函数分别从灰度图得到RGB三通道的图像,并进行合成,得到为伪彩色图像。

 

根据温度颜色,图像中较暗的区域映射为蓝色,较亮的区域映射为红色。

3.4.3  频域伪彩色增强法

把灰度图像经过傅里叶变换到频域,利用三个滤波器得到不同频率分量的单色图像,并作为RGB三个通道进行合成,得到伪彩色图像。

3.4.4  applyColorMap

/**********************************

@函数名称:color_map

@函数输入:

@函数输出:

@函数功能:预定义伪彩色增强

**********************************/

void color_map(void) {

cv::Mat srcImage = cv::imread("./img/saito.png",cv::IMREAD_GRAYSCALE);

cv::imshow("srcImage", srcImage);

 

cv::Mat imageColor[12];

for (int i = 0; i < 12; i++) {

cv::applyColorMap(srcImage, imageColor[i], i);

cv::imshow("dstImage", imageColor[i]);

cv::waitKey(0);

}

cv::destroyAllWindows();

}

3.4.5  LUT

/**********************************

@函数名称:CreateLookupTable

@函数输入:

@函数输出:

@函数功能:制作查询表

**********************************/

void CreateLookupTable(cv::Mat& table) {

table.create(1, 256, CV_8UC1);

uchar* p = table.data;

 

for (int j = 0; j < 85; j++) {

p[j] = 43;

}

for (int j = 85; j < 170; j++) {

p[j] = 127;

}

for (int j = 170; j < 255; j++) {

p[j] = 213;

}

}

/**********************************

@函数名称:color_single

@函数输入:

@函数输出:

@函数功能:自定义伪彩色增强

单通道图像处理

**********************************/

void color_single(void) {

cv::Mat table; // 定义查找表

cv::Mat src = cv::imread("./img/shi.png", cv::IMREAD_GRAYSCALE);

cv::Mat dst;

cv::imshow("srcImage", src);

 

CreateLookupTable(table);

cv::LUT(src, table, dst);

 

cv::waitKey(0);

cv::destroyAllWindows();

}

 

 

 

 

4  图像去噪

4.1  常见噪声模型与滤波模型

噪声:

加性噪声:

 

此类噪声与图像信号强度无关的。如传输过程中引入的信道噪声。

乘性噪声:

 

此类噪声和图像的信号是有关的,往往随图像信号的变化而变化。如电视扫描光栅。

量化噪声:

此类噪声是数字噪声的主要噪声来源。较好的办法是采用按灰度级概率函数选择量化级的最优化措施。

椒盐噪声:

辞此类噪声是在图像传输和处理的过程中引入的噪声。如在变换域时引入噪声。

具体噪声模型:

高斯噪声、脉冲噪声(椒盐噪声)、瑞利噪声、伽马噪声、指数分布噪声、均匀噪声;

滤波:

空域滤波:均值滤波、中值滤波、维纳滤波;

变换域滤波:傅里叶变换、余弦变换、小波变换;

4.1.1  addSaltNoise

/**********************************

@函数名称:addSaltNoise

@函数输入:

@函数输出:Mat图像

@函数功能:增加图像噪声

**********************************/

cv::Mat addSaltNoise(const cv::Mat srcImage, int n) {

cv::Mat resultImage = srcImage.clone();

for (int k = 0; k < n; k++) {

/* 随机取值行列 */

int i = rand() % resultImage.cols;

int j = rand() % resultImage.rows;

/* 图像通道判定 */

if (resultImage.channels() == 1) {

resultImage.at<uchar>(j, i) = 255;

}

else {

resultImage.atVec3b>(j, i)[0] = 255;

resultImage.atVec3b>(j, i)[1] = 255;

resultImage.atVec3b>(j, i)[2] = 255;

}

}

return resultImage;

}

4.2  主要滤波函数

4.2.1  均值滤波

 

 

均值滤波适合除去图像中的加性噪声,但由于其不能很好的保护图像中的细节,因此,在图像去噪过程中,容易破坏图像的细节信息。,而是图像显得模糊。

4.2.2  中值滤波

 

中值滤波在处理脉冲噪声方面非常有效,同时还能保护图像尖锐的边缘。,但还是会平滑图像的边缘,因为图像边缘和噪声一样,存在灰度值的“跃变”。

4.2.3  高斯滤波

 

 

4.2.4  双边滤波

 

4.3  filter API

4.3.1  filter 1

/* 图像去噪 全局变量 */

// 存储图像的Mat类型

cv::Mat g_srcImage, g_dstImage1, g_dstImage2, g_dstImage3, g_dstImage4;

int g_nBoxFilterPara = 5; // 方框滤波参数值

int g_nMeanBlurPara = 5; // 均值滤波参数值

int g_nGaussianBlurPara = 5;// 高斯滤波参数值

int g_nMedianBlurPara = 5; // 中值滤波参数值

 

/* 方框滤波操作的回调函数 */

static void boxFilterCallback(int, void*) {

cv::boxFilter(g_srcImage, g_dstImage1, -1

, cv::Size(g_nBoxFilterPara + 1, g_nBoxFilterPara + 1)

, cv::Point(-1, -1), false, cv::BORDER_DEFAULT

);

cv::imshow("dstBoxFilter", g_dstImage1);

}

 

/* 均值滤波操作的回调函数 */

static void meanBlurCallback(int, void*) {

cv::blur(g_srcImage, g_dstImage2

, cv::Size(g_nMeanBlurPara + 1, g_nMeanBlurPara + 1)

, cv::Point(-1, -1)

);

cv::imshow("dstMeanBlur", g_dstImage2);

}

 

/* 高斯滤波操作的回调函数 */

static void gaussianBlurCallback(int, void*) {

cv::GaussianBlur(g_srcImage, g_dstImage3

, cv::Size(g_nGaussianBlurPara * 2 + 1, g_nGaussianBlurPara * 2 + 1), 0, 0);

cv::imshow("dstGaussianBlur", g_dstImage3);

}

 

/* 中值滤波操作的回调函数 */

static void medianBlurCallback(int, void*) {

cv::medianBlur(g_srcImage, g_dstImage4, g_nMedianBlurPara);

cv::imshow("dstMedianBlur", g_dstImage4);

}

4.3.2  filter 2

/**********************************

@函数名称:reduce_noise

@函数输入:

@函数输出:

@函数功能:图像去噪

**********************************/

void reduce_noise(void) {

/* 原图加噪处理 */

cv::Mat srcImage = cv::imread("E:\\cpp\\opencvCPP\\runOne\\img\\shi.png", cv::IMREAD_GRAYSCALE);

g_srcImage = addSaltNoise(srcImage, 5000);

 

if (g_srcImage.empty()) { // 防错处理

std::cout << "srcImageerror!" << std::endl;

return;

}

g_dstImage1 = g_srcImage.clone(); // 克隆原图到4个Mat类型中

g_dstImage2 = g_srcImage.clone();

g_dstImage3 = g_srcImage.clone();

g_dstImage4 = g_srcImage.clone();

 

cv::namedWindow("srcImage", 1); // 显示原图

cv::imshow("srcImage", srcImage);

 

cv::namedWindow("srcAddNoise", 1); // 显示加噪声图像

cv::imshow("srcAddNoise", g_srcImage);

 

/* 方框滤波 */

cv::namedWindow("dstBoxFilter", 1);

cv::createTrackbar("Kernal Parameter: ", "dstBoxFilter", &g_nBoxFilterPara, 40, boxFilterCallback);

boxFilterCallback(g_nBoxFilterPara, 0);

// cv::imshow("dstBoxFilter", g_dstImage1);

 

/* 均值滤波 */

cv::namedWindow("dstMeanBlur", 1);

cv::createTrackbar("Kernal Parameter: ", "dstMeanBlur", &g_nMeanBlurPara, 40, meanBlurCallback);

meanBlurCallback(g_nMeanBlurPara, 0);

// cv::imshow("dstBoxFilter", g_dstImage2);

 

/* 高斯滤波 */

cv::namedWindow("dstGaussianBlur", 1);

cv::createTrackbar("Kernal Parameter: ", "dstGaussianBlur", &g_nGaussianBlurPara, 40, gaussianBlurCallback);

gaussianBlurCallback(g_nGaussianBlurPara, 0);

// cv::imshow("dstBoxFilter", g_dstImage3);

 

/* 中值滤波 */

cv::namedWindow("dstMedianBlur", 1);

cv::createTrackbar("Kernal Parameter: ", "dstMedianBlur", &g_nMedianBlurPara, 40, medianBlurCallback);

medianBlurCallback(g_nMedianBlurPara, 0);

cv::imshow("dstMedianBlur", g_dstImage4);

 

cv::waitKey(0);

//cv::destroyAllWindows();

}

4.3.3  filter 3

/**********************************

@函数名称:filter_image

@函数输入:none

@函数输出:none

@函数功能:均值滤波-高斯滤波

**********************************/

void filter_image(void) {

cv::Mat src = cv::imread("./img/shiyuan.png");

char srcTittle[] = "srcImage";

cv::imshow(srcTittle, src);

 

/* 均值滤波-均值模糊 */

cv::Mat dst_blur;

dst_blur.create(src.size(), src.type());

dst_blur = cv::Scalar(0, 0, 0);

 

cv::blur(src, dst_blur, cv::Size(7, 7), cv::Point(-1, -1));

// Point(-1, -1)表示取核的中心为锚点

cv::imshow("blur filter", dst_blur);

 

/* 高斯滤波 */

cv::Mat dst_gaussian = cv::Mat::zeros(src.size(), src.type());

cv::GaussianBlur(src, dst_gaussian, cv::Size(7, 7), 11, 11);

// sigmaX/Y:高斯核函数在XY方向上的标准偏差

cv::imshow("gaussion filter", dst_gaussian);

 

cv::waitKey(0);

cv::destroyAllWindows();

}

4.3.4  filter 4

/**********************************

@函数名称:mid_double_filter

@函数输入:none

@函数输出:none

@函数功能:中值滤波-双边滤波

**********************************/

void mid_double_filter(void) {

/* 原图像 */

cv::Mat src = cv::imread("./img/shiyuan.png");

if (!src.data) { // src.empty()

std::cerr << "error input" << std::endl;

return;

}

cv::imshow("srcImage", src);

 

/* 中值滤波 */

cv::Mat dst_med;

cv::medianBlur(src, dst_med, 3);

cv::imshow("medfilter", dst_med);

 

/* 双边滤波 */

cv::Mat dst_double;

cv::bilateralFilter(src, dst_double, 15, 100, 5);

cv::imshow("bifilter", dst_double);

 

/* 高斯滤波 */

cv::Mat dst_gaussian = cv::Mat::zeros(src.size(), src.type());

cv::GaussianBlur(src, dst_gaussian, cv::Size(15, 15), 3, 3);

// sigmaX/Y:高斯核函数在XY方向上的标准偏差

cv::imshow("gaussion filter", dst_gaussian);

 

/* 自定义滤波 */

cv::Mat resultImg;

cv::Mat kernel = (cv::Mat_<int>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);

filter2D(dst_double, resultImg, -1, kernel, cv::Point(-1, -1), 0);

imshow("Final Result", resultImg);

 

cv::waitKey(0);

cv::destroyAllWindows();

}

4.4  其他滤波方法

K近邻滤波:

也称边界保持滤波,即判断需要处理的点是否在图像边界,不是则平滑处理,是则保留,这样能够有效保护图像边界细节信息。

维纳滤波:

也称最小二乘滤波和最小平方滤波,使输出图像和理想图像的均方误差最小。

小波变换滤波、Lee滤波、Frost滤波

 

5  边缘提取

5.1  Robert算子

一阶微分,X/Y轴上计算梯度,梯度算子

 

左:X轴上最大差异;

右:Y轴上最大差异;

5.1.1  All Filter Function

 

/**********************************

@函数名称:filter_self

@函数输入:none

@函数输出:按ESC退出

@函数功能:自定义阈值滤波

**********************************/

void filter_self(void) {

/* 加载原始图像 */

cv::Mat src;

src = cv::imread("./img/saito.png");

if (!src.data) {

printf("could not load image...\n");

return;

}

char INPUT_WIN[] = "input image";

cv::namedWindow(INPUT_WIN, CV_WINDOW_AUTOSIZE);

imshow(INPUT_WIN, src);

 

/* Robert算子 X轴方向 */

cv::Mat robert_X_dst;

cv::Mat kernel_robert_X = (cv::Mat_<int>(2, 2) << 1, 0, 0, -1);

cv::filter2D(src, robert_X_dst, src.depth(), kernel_robert_X, cv::Point(-1, -1), 0.0);

cv::imshow("Robert_X Filter Result Image", robert_X_dst);

 

/* Robert算子 Y轴方向 */

cv::Mat robert_Y_dst;

cv::Mat kernel_robert_Y = (cv::Mat_<int>(2, 2) << 0, 1, -1, 0);

cv::filter2D(src, robert_Y_dst, src.depth(), kernel_robert_Y, cv::Point(-1, -1), 0.0);

cv::imshow("Robert_Y Filter Result Image", robert_Y_dst);

 

/* Sobel算子 X轴方向 */

cv::Mat sobel_X_dst;

cv::Mat kernel_sobel_X = (cv::Mat_<int>(3, 3) << -1, 0, 1, -2, 0, 2, -1, 0, 1);

cv::filter2D(src, sobel_X_dst, src.depth(), kernel_sobel_X, cv::Point(-1, -1), 0.0);

cv::imshow("Sobel_X Filter Result Image", sobel_X_dst);

 

/* Sobel算子 Y轴方向 */

cv::Mat sobel_Y_dst;

cv::Mat kernel_sobel_Y = (cv::Mat_<int>(3, 3) << -1, -2, -1, 0, 0, 0, 1, 2, 1);

cv::filter2D(src, sobel_Y_dst, src.depth(), kernel_sobel_Y, cv::Point(-1, -1), 0.0);

cv::imshow("Sobel_Y Filter Result Image", sobel_Y_dst);

 

/* laplance算子 */

cv::Mat laplance_dst;

cv::Mat kernel_laplance = (cv::Mat_<int>(3, 3) << 0, -1, 0, -1, 4, -1, 0, -1, 0);

cv::filter2D(src, laplance_dst, src.depth(), kernel_laplance, cv::Point(-1, -1), 0.0);

cv::imshow("Laplance Filter Result Image", laplance_dst);

 

/* 自定义算子模糊 */

int c = 0; // 按键输入

int ksize = 0; // 核尺寸,动态变化

int index = 0; // 递增的序号

cv::Mat self_dst;

 

while (1) {

c = cv::waitKey(1000);

if ((char)c == 27) { // ESC

break;

}

ksize = 5 + (index % 8) * 2; // 根据递增的序号,计算核的大小

cv::Mat kernel_self = cv::Mat::ones(cv::Size(ksize, ksize), CV_32F) / (float)(ksize * ksize);

// 根据核大小,生成全1的核

cv::filter2D(src, self_dst, -1, kernel_self, cv::Point(-1,-1));

index++;

cv::imshow("self filter", self_dst);

}

 

cv::waitKey(0);

}

5.2  Sobel算子

一阶微分,效果比Ronert明显。数字2是为了扩大差异。

 

左:X水平上差异;

右:Y垂直上差异;

 

图像边缘位置,导数函数有变化。

 

 

5.2.1  example code 1

/**********************************

@函数名称:Sobel_function

@函数输入:

@函数输出:

@函数功能:Sobel算子

**********************************/

void Sobel_function(void) {

cv::Mat src;

cv::Mat grad;

int scale = 1;

int delta = 0;

int ddepth = CV_16S;

 

src = cv::imread("./img/saito.png", cv::IMREAD_GRAYSCALE);

if (src.empty())

return;

 

cv::Mat grad_x, grad_y;

cv::Mat abs_grad_x, abs_grad_y;

 

cv::Sobel(src, grad_x, ddepth, 1, 0, 3, scale, delta, cv::BORDER_DEFAULT);

cv::Sobel(src, grad_y, ddepth, 0, 1, 3, scale, delta, cv::BORDER_DEFAULT);

 

cv::convertScaleAbs(grad_x, abs_grad_x);

cv::convertScaleAbs(grad_y, abs_grad_y);

cv::addWeighted(abs_grad_x, 0.5, abs_grad_y, 0.5, 0, grad);

 

cv::imshow("srcImage", src);

cv::imshow("dstImage", grad);

cv::waitKey(0);

cv::destroyAllWindows();

}

5.2.2  sobel&scharr提取边界

 

 

 

 

/**********************************

@函数名称:sobel_image

@函数输入:none

@函数输出:none

@函数功能:sobel求导。提取边界

**********************************/

void sobel_image(void) {

/* 原始图像 */

cv::Mat src;

src = cv::imread("./img/shiyuan.png");

if (!src.data) {

printf("could not load image...\n");

return;

}

char INPUT_TITLE[] = "input image";

cv::namedWindow(INPUT_TITLE, CV_WINDOW_AUTOSIZE);

imshow(INPUT_TITLE, src);

/* 先图像预处理 */

cv::Mat gau_dst;

cv::GaussianBlur(src, gau_dst, cv::Size(3, 3), 0, 0); // 先做高斯模糊

cv::cvtColor(gau_dst, gau_dst, CV_BGR2GRAY); // 转换成灰度图

cv::imshow("GrayImage", gau_dst);

 

/* sobel求导,提取边界信息 */

cv::Mat Xgrad, Ygrad;

// cv::Scharr(gau_dst, Xgrad, CV_16S, 1, 0); // 加强版Sobel

// cv::Scharr(gau_dst, Ygrad, CV_16S, 0, 1);

cv::Sobel(gau_dst, Xgrad, CV_16S, 1, 0, 3); // -1表示相同,CV_32F/CV_64F,越大效果越好

// 输入,输出,数值类型,X轴,Y轴,核大小

cv::Sobel(gau_dst, Ygrad, CV_16S, 0, 1, 3);

cv::convertScaleAbs(Xgrad, Xgrad);

cv::convertScaleAbs(Ygrad, Ygrad);

cv::imshow("Xgrad", Xgrad);

cv::imshow("Ygrad", Ygrad);

 

/* XY相加*/

cv::Mat final_dst;

char OUTPUT_TITLE[] = "sobel-demo";

cv::namedWindow(OUTPUT_TITLE, CV_WINDOW_AUTOSIZE);

cv::addWeighted(Xgrad, 0.5, Ygrad, 0.5, 0, final_dst);

cv::imshow(OUTPUT_TITLE, final_dst);

 

cv::Mat xygrad = cv::Mat(Xgrad.size(), Xgrad.type()); // 不能用src原图像的size

std::cout << "type: " << Xgrad.type() << std::endl;

int width = Xgrad.cols; // 是X方向的列-宽,一样的

int height = Ygrad.rows; // 是Y方向的行-高

 

/* 指针操作像素相加 */

for (int row = 0; row < height; row++) {

for (int col = 0; col < width; col++) {

int xgv = Xgrad.at<uchar>(row, col); // 要uchar,不要char,数据丢失

int ygv = Ygrad.at<uchar>(row, col);

int xygv = xgv + ygv;

xygrad.at<uchar>(row, col) = cv::saturate_cast<uchar>(xygv);

}

}

cv::imshow("at ptr add", xygrad);

cv::waitKey(0);

cv::destroyAllWindows();

}

5.3  Priwitt算子

一阶微分

 

 

5.3  Laplance算子

二阶微分,能够图像的边缘都提取到。

 

整个方向上的差异;

 

 

 

 

5.3.1  example code 1

/**********************************

@函数名称:Laplacian_function

@函数输入:

@函数输出:

@函数功能:图像锐化-拉普拉斯算子API

**********************************/

void Laplacian_function(void) {

cv::Mat src;

cv::Mat dst;

src = cv::imread("./img/shi.png");

if (src.empty())

return;

 

cv::Laplacian(src, dst, 0, 3);

cv::imshow("srcImage", src);

cv::imshow("dstImage", dst);

cv::waitKey(0);

cv::destroyAllWindows();

}

5.3.2  example code 2

/**********************************

@函数名称:Laplacian_image

@函数输入:

@函数输出:

@函数功能:图像锐化-自定义拉普拉斯算子

**********************************/

void Laplacian_image(void) {

cv::Mat src;

cv::Mat dst1;

cv::Mat dst2;

 

src = cv::imread("./img/saito.png");

if (src.empty()) {

return;

}

cv::Mat kernel1 = (cv::Mat_<float>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);

cv::Mat kernel2 = (cv::Mat_<float>(3, 3) << 0, 1, 0, 1, -4, 1, 0, 1, 0);

cv::filter2D(src, dst1, CV_8UC3, kernel1);

cv::filter2D(src, dst2, CV_8UC3, kernel2);

 

cv::imshow("srcImage", src);

cv::imshow("dstImage1", dst1);

cv::imshow("dstImage2", dst2);

 

cv::waitKey(0);

cv::destroyAllWindows();

}

5.3.3  example code 3

/**********************************

@函数名称:laplance_function

@函数输入:none

@函数输出:none

@函数功能:拉普拉斯提取图像边界

**********************************/

void laplance_function(void) {

/* 原始图像 */

cv::Mat src;

src = cv::imread("./img/shiyuan.png");

if (!src.data) {

printf("could not load image...\n");

return;

}

char INPUT_TITLE[] = "input image";

cv::namedWindow(INPUT_TITLE, CV_WINDOW_AUTOSIZE);

imshow(INPUT_TITLE, src);

 

/* 先高斯模糊,去除小的噪声 */

cv::Mat dst_gaussian;

cv::GaussianBlur(src, dst_gaussian, cv::Size(3, 3), 0, 0);

cv::cvtColor(dst_gaussian, dst_gaussian, CV_BGR2GRAY); // 转灰色

cv::imshow("grayImage", dst_gaussian);

 

/* 拉普拉斯提取边缘信息 */

cv::Mat dst_edge;

cv::Laplacian(dst_gaussian, dst_edge, CV_16S, 3); // 注意数据类型

cv::convertScaleAbs(dst_edge, dst_edge); // 必须数据转换成8位数据

cv::threshold(dst_edge, dst_edge, 0, 255, cv::THRESH_OTSU | cv::THRESH_BINARY);  // 加强显示效果

cv::imshow("LaplanceImage", dst_edge);

 

cv::waitKey(0);

cv::destroyAllWindows();

}

5.4  Canny算子

输入需要是8位图像,早期函数顶死。

 

 

 

 

5.4.1  example code 1

/**********************************

@函数名称:Canny_filter

@函数输入:

@函数输出:

@函数功能:Canny算子

**********************************/

void Canny_filter(void) {

cv::Mat srcImage = cv::imread("./img/shi.png");

cv::imshow("srcImage", srcImage);

 

cv::Mat grayImage;

cv::cvtColor(srcImage, grayImage, cv::COLOR_BGR2GRAY);

cv::imshow("grayImage", grayImage);

 

cv::Mat midImage;

grayImage.copyTo(midImage);

 

/* 对灰度图和三通道图进行双边滤波 */

cv::bilateralFilter(midImage, grayImage, 7, 14, 4);

// cv::imshow("dstmidImage", midImage);

// cv::imshow("dstgrayImage", grayImage);

 

cv::Mat cannyImage;

cv::Canny(grayImage, cannyImage, 100, 150, 3);

cv::imshow("dstCanny", cannyImage);

 

cv::waitKey(0);

cv::destroyAllWindows();

}

5.4.2  example code 2

/**********************************

@函数名称:laplance_function

@函数输入:none

@函数输出:none

@函数功能:Canny提取图像边缘

**********************************/

int tValue_1 = 50;

int maxValue = 255;

cv::Mat dst_gray_canny;

//cv::Mat src; // 上面定义

cv::Mat dst;

const char* outputWindow = "Canny Result";

// ----------------------------- //

void canny_callback(int, void*) {

/* 模糊平滑-降低图像噪声 */

cv::Mat dst_edge;

cv::blur(dst_gray_canny, dst_gray_canny, cv::Size(3, 3), cv::Point(-1, -1), cv::BORDER_DEFAULT);

 

/* canny提取边界 */

cv::Canny(dst_gray_canny, dst_edge, tValue_1, tValue_1 * 2, 3, false);

// 输入,输出,t1,t2,核尺寸,false-L1正则化

cv::imshow("canny Image", dst_edge); // 可取反显示

 

/* 掩码 copy 显示 */

// cv::Mat dst; // 全局定义

dst.create(src.size(), src.type());

src.copyTo(dst, dst_edge); // 用copyTo做图像掩膜操作

// 依据下面实例,边缘是白色1,需要保留的部分,背景为黑色0与结果对应

/* 举例说明

* srcImage.copyto(dstImage, mask)

* mask背景为黑色0,需要显示的内容白色1

* 当mask值为1,将src的值给dst

* 当mask值为0,保留dst的值

*/

cv::imshow(outputWindow, ~dst); // 可取反显示,这里取反效果好

}

 

/**********************************

@函数名称:laplance_function

@函数输入:需要时8位图像

@函数输出:none

@函数功能:Canny提取图像边缘

**********************************/

void canny_image(void) {

/* 原始图像 */

// cv::Mat src; // 全局定义

src = cv::imread("./img/shiyuan.png");

if (!src.data) {

printf("could not load image...\n");

return;

}

char INPUT_TITLE[] = "input image";

cv::namedWindow(INPUT_TITLE, CV_WINDOW_AUTOSIZE);

cv::namedWindow(outputWindow, CV_WINDOW_AUTOSIZE); // 必须先初始化结果窗口

imshow(INPUT_TITLE, src);

 

/* 转色域 */

cv::cvtColor(src, dst_gray_canny, CV_BGR2GRAY);

cv::imshow("grayImage", dst_gray_canny);

 

/* 创建滑条 canny function */

cv::createTrackbar("Threshold Value:", outputWindow, &tValue_1, maxValue, canny_callback);

canny_callback(0, 0);

 

cv::waitKey(0);

cv::destroyAllWindows();

}

 

5.5  边缘处理

 

 

5.5.1  copyMakeBorder

 

/**********************************

@函数名称:make_border

@函数输入:none

@函数输出:ESC退出

@函数功能:边界填充

**********************************/

void make_border(void) {

/* 加载原图 */

cv::Mat src;

src = cv::imread("./img/shiyuan.png");

if (!src.data) {

printf("could not load image...\n");

return;

}

char INPUT_WIN[] = "input image";

char OUTPUT_WIN[] = "Border Demo";

cv::namedWindow(INPUT_WIN, CV_WINDOW_AUTOSIZE);

cv::namedWindow(OUTPUT_WIN, CV_WINDOW_AUTOSIZE);

imshow(INPUT_WIN, src);

 

/* 添加边界 */

int top    = (int)(0.05 * src.rows); // 上-行

int bottom = (int)(0.05 * src.rows); // 下-行

int left   = (int)(0.05 * src.cols); // 左-列

int right  = (int)(0.05 * src.cols); // 右-列

cv::RNG rng(12345); // 定义随机数

int borderType = cv::BORDER_DEFAULT; // 填充边界类型

int c = 0; // 按键

cv::Mat border_dst;

 

while (true) {

c = cv::waitKey(500);

if ((char)c == 27) { // 退出

break;

}

 

if ((char)c == 'r') { // 边界填充类型

borderType = cv::BORDER_REPLICATE;

}

else if ((char)c == 'w') {

borderType = cv::BORDER_WRAP;

}

else if ((char)c == 'c') {

borderType = cv::BORDER_CONSTANT;

}

cv::Scalar color = cv::Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255)); // 随机颜色

copyMakeBorder(src, border_dst, top, bottom, left, right, borderType, color);

cv::imshow(OUTPUT_WIN, border_dst);

}

cv::Mat dst;

cv::GaussianBlur(src, dst, cv::Size(5, 5), 0, 0, borderType); // 模糊的时候加边界填充类型

cv::imshow(OUTPUT_WIN, dst);

cv::waitKey(0);

}

5.5.2  自适应阈值分割

/**********************************

@函数名称:adaptive_threshold

@函数输入:

@函数输出:

@函数功能:自适应阈值分割

**********************************/

void adaptive_threshold(void) {

cv::Mat srcImage = cv::imread("./img/shi.png",cv::IMREAD_GRAYSCALE);

cv::imshow("srcImage", srcImage);

 

cv::Mat dstImage;

int maxVal = 255;

int blockSize = 9;

double C = 0;

 

cv::adaptiveThreshold(srcImage, dstImage, maxVal

, cv::ADAPTIVE_THRESH_MEAN_C, cv::THRESH_BINARY, blockSize, C);

 

cv::imshow("dst:blockSize=9", dstImage);

cv::waitKey(0);

cv::destroyAllWindows();

}

 

 

6  图像形态学

6.1  膨胀

 

6.2  腐蚀

 

6.2.1  dilate&erode 1

 

/**********************************

@函数名称:pengzhangfushi_image

@函数输入:

@函数输出:

@函数功能:膨胀与腐蚀

**********************************/

void pengzhangfushi_image(void) {

cv::Mat srcImage;

cv::Mat dstImage_F;

cv::Mat dstImage_P;

srcImage = cv::imread("./img/shi.png", 1);

if (srcImage.empty())

return;

 

cv::Mat ele = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5));// 定义内核矩阵

cv::erode(srcImage, dstImage_F, ele);

cv::dilate(srcImage, dstImage_P, ele);

 

cv::imshow("srcImage", srcImage);

cv::imshow("dstImage_F", dstImage_F);

cv::imshow("dstImage_P", dstImage_P);

 

cv::waitKey(0);

cv::destroyAllWindows();

}

6.2.2  dilate&erode 2

/**********************************

@函数名称:CallBack_Demo_dilate

@函数输入:none

@函数输出:none

@函数功能:膨胀

**********************************/

cv::Mat src, dst_dilate, dst_erode;

char window_dilate[] = "dilate_dst";

char window_erode[] = "erode_dst";

int element_size = 3;

int maxsize = 15;

// -------------------------------//

void CallBack_Demo_dilate(int, void*) {

int s = element_size * 2 + 1;

cv::Mat structureElement = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(s, s), cv::Point(-1, -1));

// 会返回指定形状和尺寸的结构元素,结构元素,内核的尺寸,中心点

cv::dilate(src, dst_dilate, structureElement, cv::Point(-1, -1), 1);

// 原图,结果图,结构元素,中心点,迭代次数

cv::imshow(window_dilate, dst_dilate);

return;

}

 

/**********************************

@函数名称:CallBack_Demo_erode

@函数输入:none

@函数输出:none

@函数功能:腐蚀

**********************************/

void CallBack_Demo_erode(int, void*) {

int s = element_size * 2 + 1;

cv::Mat structureElement = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(s, s), cv::Point(-1, -1));

// 会返回指定形状和尺寸的结构元素,结构元素,内核的尺寸,中心点

cv::erode(src, dst_erode, structureElement, cv::Point(-1, -1), 1);

// 原图,结果图,结构元素,中心点,迭代次数

cv::imshow(window_erode, dst_erode);

return;

}

 

/**********************************

@函数名称:filter_image

@函数输入:none

@函数输出:none

@函数功能:膨胀 & 腐蚀

**********************************/

void dilate_erode(void) {

/* 原图像 */

src = cv::imread("./img/shiyuan.png");

if (!src.data) { // src.empty()

std::cerr << "error input" << std::endl;

return;

}

cv::imshow("srcImage", src);

 

/* 初始化 */

cv::namedWindow(window_dilate, CV_WINDOW_AUTOSIZE);

cv::namedWindow(window_erode, CV_WINDOW_AUTOSIZE);

 

/* 滑条窗口处理 */

cv::createTrackbar("Dilate element size:", window_dilate, &element_size, maxsize, CallBack_Demo_dilate);

CallBack_Demo_dilate(0, 0);

 

cv::createTrackbar("Erode element size:", window_erode, &element_size, maxsize, CallBack_Demo_erode);

CallBack_Demo_erode(0, 0);

 

cv::waitKey(0);

cv::destroyAllWindows();

}

6.3  开运算

 

 

 

6.4  闭运算

 

 

 

 

6.4.1  open&close 1

/**********************************

@函数名称:kaibi_image

@函数输入:

@函数输出:

@函数功能:开闭运算

**********************************/

void kaibi_image(void) {

cv::Mat srcImage;

cv::Mat dstImage_F;

cv::Mat dstImage_P;

srcImage = cv::imread("./img/shi.png", 1);

 

if (srcImage.empty())

return;

 

cv::Mat ele = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5));

/* 开运算,先服饰,后膨胀 */

cv::erode(srcImage, dstImage_F, ele);

cv::dilate(dstImage_F, dstImage_P, ele);

 

cv::imshow("srcImage", srcImage);

// cv::imshow("dstImage_F", dstImage_F);

cv::imshow("dstImage_P", dstImage_P);

 

cv::waitKey(0);

cv::destroyAllWindows();

}

6.5  形态学梯度

 

 

6.6  顶帽运算

 

6.7  黑帽运算

 

 

6.7.1  open&close&top&black

/**********************************

@函数名称:open_close

@函数输入:none

@函数输出:none

@函数功能:开 闭 顶帽 黑帽

**********************************/

void open_close(void) {

/* 原图像 */

cv::Mat src = cv::imread("./img/pointpic.jpg");

if (!src.data) {

std::cerr << "error input" << std::endl;

return;

}

cv::namedWindow("srcImage", CV_WINDOW_AUTOSIZE);

cv::imshow("srcImage", src);

 

/* 初始化 */

cv::Mat kernel, dst_open, dst_close, dst_top, dst_black, dst_geo;

 

/* 开运算 */

kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(11, 11), cv::Point(-1, -1));

// 去除小点,size控制效果

cv::morphologyEx(src, dst_open, CV_MOP_OPEN, kernel);

imshow("dst_open", dst_open);

 

/* 闭运算 */

kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(21, 21), cv::Point(-1, -1));

cv::morphologyEx(src, dst_close, CV_MOP_CLOSE, kernel);

imshow("dst_close", dst_close);

 

/* 形态学梯度 */

kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3), cv::Point(-1, -1));

cv::morphologyEx(src, dst_geo, CV_MOP_GRADIENT, kernel);

imshow("dst_geo_binary", dst_geo);

 

cv::Mat dst_geo_color;

cv::Mat src_geo_color = cv::imread("./img/saito.png");

kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3), cv::Point(-1, -1));

cv::morphologyEx(src_geo_color, dst_geo_color, CV_MOP_GRADIENT, kernel);

imshow("dst_geo_color", dst_geo_color);

 

/* 顶帽运算 */

kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3), cv::Point(-1, -1));

cv::morphologyEx(src, dst_top, CV_MOP_TOPHAT, kernel);

imshow("dst_top_binary", dst_top);

 

cv::Mat dst_top_color;

cv::Mat src_top_color = cv::imread("./img/saito.png");

kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3), cv::Point(-1, -1));

cv::morphologyEx(src_top_color, dst_top_color, CV_MOP_TOPHAT, kernel);

imshow("dst_top_color", dst_top_color);

 

/* 黑帽运算 */

kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3), cv::Point(-1, -1));

cv::morphologyEx(src, dst_black, CV_MOP_BLACKHAT, kernel);

imshow("dst_black", dst_black);

 

cv::waitKey(0);

cv::destroyAllWindows();

}

6.8  提取水平和垂直线

 

 

 

先腐蚀,用长方形线,用黑色最小值,代替白色最大值,也就没有了垂直白色线。

6.8.1  example code

 

 

/**********************************

@函数名称:line_image

@函数输入:none

@函数输出:none

@函数功能:提取水平和垂直直线

**********************************/

void line_image(void) {

/* 加载原图像 */

cv::Mat srcImage = cv::imread("./img/point.png");

if (srcImage.empty()) {

std::cerr << "error input" << std::endl;

return;

}

cv::imshow("srcImage", srcImage);

 

/* 转为灰度图像 */

cv::Mat gray_dst;

cv::cvtColor(srcImage, gray_dst, CV_BGR2GRAY);

cv::imshow("grayImage", gray_dst);

 

 

/* 转为二值化图像 */

cv::Mat bin_dst;

cv::adaptiveThreshold(~gray_dst, bin_dst, 255, CV_ADAPTIVE_THRESH_MEAN_C, cv::THRESH_BINARY, 15, -2);

// 取反正常些,转换背景色

// 灰度输入,二值化输出,最大值,阈值计算方法,阈值类型,核大小,调整偏移量

cv::imshow("binImage", bin_dst);

 

/* 提取水平线 */

cv::Mat hline = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(srcImage.cols / 16, 1), cv::Point(-1, -1));

// size:列,行

// 内核形状,内核尺寸,锚点位置

cv::Mat temp, hline_dst;

// cv::erode(bin_dst, temp, hline); // 开运算先腐蚀,后膨胀

// cv::dilate(temp, hline_dst, hline);

cv::morphologyEx(bin_dst, hline_dst, CV_MOP_OPEN, hline);

cv::bitwise_not(hline_dst, hline_dst); // 背景白

cv::blur(hline_dst, hline_dst, cv::Size(3, 3), cv::Point(-1, -1)); // 均值模糊-均值滤波

cv::imshow("hline", hline_dst);

 

/* 提取垂直线 */

cv::Mat vline = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(1, srcImage.rows / 16), cv::Point(-1, -1));

cv::Mat vline_dst;

// cv::erode(bin_dst, temp, vline);

// cv::dilate(temp, vline_dst, vline);

cv::morphologyEx(bin_dst, vline_dst, CV_MOP_OPEN, vline); // 也是开运算,不是闭运算

cv::bitwise_not(vline_dst, vline_dst); // 背景白

cv::blur(vline_dst, vline_dst, cv::Size(3, 3), cv::Point(-1, -1)); // 均值模糊-均值滤波

cv::imshow("vline", vline_dst);

 

/* 去除干扰 */

cv::Mat dst;

cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3), cv::Point(-1, -1));

// 只能去除文字的细线和细点,太粗的效果不好,调大尺寸也不行

cv::morphologyEx(bin_dst, dst, CV_MOP_OPEN, kernel);

cv::imshow("dstImage", dst);

 

cv::waitKey(0);

cv::destroyAllWindows();

}

 

 

 

 

 

 

你可能感兴趣的:(OpenCV,cv)