opencv入门基础(c++)【三】

十三、图像上采样和降采样

13.1图像金字塔概念

  1. 我们在图像处理中常常会调整图像大小,最常见的就是放大(zoom in)和缩小(zoom out),尽管几何变换也可以实现图像放大和缩小,但是这里我们介绍图像金字塔
  2. 一个图像金字塔式一系列的图像组成,最底下一张是图像尺寸最大,最上方的图像尺寸最小,从空间上从上向下看就想一个古代的金字塔。
    在这里插入图片描述
    高斯金子塔 – 用来对图像进行降采样
    拉普拉斯金字塔 – 用来重建一张图片根据它的上层降采样图片

图像金字塔概念 – 高斯金字塔(变大变小)

高斯金子塔是从底向上,逐层降采样得到。
降采样之后图像大小是原图像MxN的M/2 x N/2 ,就是对原图像删除偶数行与列,即得到降采样之后上一层的图片。
高斯金子塔的生成过程分为两步:
- 对当前层进行高斯模糊
- 删除当前层的偶数行与列
即可得到上一层的图像,这样上一层跟下一层相比,都只有它的1/4大小。
在这里插入图片描述

高斯不同(Difference of Gaussian-DOG)(变暗提取轮廓)

定义:就是把同一张图像在不同的参数下做高斯模糊之后的结果相减,得到的输出图像。称为高斯不同(DOG)
高斯不同是图像的内在特征,在灰度图像增强、角点检测中经常用到。




#include <opencv2/opencv.hpp>
#include <opencv2/imgproc.hpp>
#include<iostream>
#include<math.h>
using namespace std;
using namespace cv;
Mat src1,dst;

char output_win[] = "output image";
int main(int argc, char** argv)
{    
	 src1 = imread("C:/Users/Admin/Desktop/1.jpg");	//读取图片(使用图片的绝对路径)	
	if (src1.empty()) {
		printf("could not find the image!\n");
		return -1;
	}

	
	char input_win[] = "input image";
	namedWindow(input_win, WINDOW_AUTOSIZE);
	imshow(input_win,src1);

	// DOG
	Mat gray_src, g1, g2, dogImg;
	cvtColor(src1, gray_src, COLOR_BGR2GRAY);
	GaussianBlur(gray_src, g1, Size(5, 5), 0, 0);
	GaussianBlur(g1, g2, Size(5, 5), 0, 0);
	subtract(g1, g2, dogImg, Mat());

	// 归一化显示
	normalize(dogImg, dogImg, 255, 0, NORM_MINMAX);
	imshow("DOG Image", dogImg);



	//imshow(output_win, dst);
	waitKey(0);//不加此语句图片会一闪而过
	system("path");
	getchar();
	return 0;
}





在这里插入图片描述

采样相关API

上采样(cv::pyrUp) – zoom in 放大
降采样 (cv::pyrDown) – zoom out 缩小

pyrUp(Mat src, Mat dst, Size(src.cols2, src.rows2))
生成的图像是原图在宽与高各放大两倍
pyrDown(Mat src, Mat dst, Size(src.cols/2, src.rows/2))
生成的图像是原图在宽与高各缩小1/2




#include <opencv2/opencv.hpp>
#include <opencv2/imgproc.hpp>
#include<iostream>
#include<math.h>
using namespace std;
using namespace cv;
Mat src1,dst;

char output_win[] = "output image";
int main(int argc, char** argv)
{    
	 src1 = imread("C:/Users/Admin/Desktop/1.jpg");	//读取图片(使用图片的绝对路径)	
	if (src1.empty()) {
		printf("could not find the image!\n");
		return -1;
	}
	// 上采样
	pyrUp(src1, dst, Size(src1.cols * 2, src1.rows * 2));
	// 降采样
	Mat s_down;
	pyrDown(src1, s_down, Size(src1.cols / 2, src1.rows / 2));

	
	char input_win[] = "input image";
	namedWindow(input_win, WINDOW_AUTOSIZE);
	imshow(input_win,src1);


	
	imshow(output_win, dst);

	namedWindow("sample down", WINDOW_AUTOSIZE);
	imshow("sample down", s_down);
	//imshow(output_win, dst);
	waitKey(0);//不加此语句图片会一闪而过
	system("path");
	getchar();
	return 0;
}





十四、基本阈值操作

图像阈值(threshold)

阈值 是什么?简单点说是把图像分割的标尺,这个标尺是根据什么产生的,阈值产生算法?阈值类型。(Binary segmentation)

在这里插入图片描述

阈值类型

阈值二值化(threshold binary)

左下方的图表示图像像素点Src(x,y)值分布情况,蓝色水平线表示阈值
在这里插入图片描述

阈值反二值化(threshold binary Inverted)

左下方的图表示图像像素点Src(x,y)值分布情况,蓝色水平线表示阈值
在这里插入图片描述

截断 (truncate)

左下方的图表示图像像素点Src(x,y)值分布情况,蓝色水平线表示阈值
在这里插入图片描述

阈值取零 (threshold to zero)

在这里插入图片描述

阈值反取零 (threshold to zero inverted)

在这里插入图片描述

threshold函数的使用

https://blog.csdn.net/u012566751/article/details/77046445

#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>

using namespace cv;
Mat src, gray_src, dst;
int threshold_value = 127;
int threshold_max = 255;
int type_value = 2;
int type_max = 4;
const char* output_title = "binary image";
void Threshold_Demo(int, void*);
int main(int argc, char** argv) {
	src = imread("C:/Users/Admin/Desktop/1.jpg");
	if (!src.data) {
		printf("could not load image...\n");
		return -1;
	}
	namedWindow("input image", WINDOW_AUTOSIZE);
	namedWindow(output_title, WINDOW_AUTOSIZE);
	imshow("input image", src);
	
	createTrackbar("Threshold Value:", output_title, &threshold_value, threshold_max, Threshold_Demo);
	createTrackbar("Type Value:", output_title, &type_value, type_max, Threshold_Demo);
	Threshold_Demo(0, 0);

	waitKey(0);
	return 0;
}

void Threshold_Demo(int, void*) {
	cvtColor(src, gray_src, COLOR_BGR2GRAY);
	threshold(src, dst, 0, 255, THRESH_TRIANGLE | type_value);
	imshow(output_title, dst);
}

十四、自定义线性滤波

14.1卷积概念

卷积是图像处理中一个操作,是kernel在图像的每个像素上的操作。
Kernel本质上一个固定大小的矩阵数组,其中心点称为锚点(anchor point)
在这里插入图片描述

14.2卷积如何工作

把kernel放到像素数组之上,求锚点周围覆盖的像素乘积之和(包括锚点),用来替换锚点覆盖下像素点值称为卷积处理。数学表达如下:
在这里插入图片描述
在这里插入图片描述
Sum = 8x1+6x1+6x1+2x1+8x1+6x1+2x1+2x1+8x1
New pixel = sum / (m*n)

14.3常见算子

在这里插入图片描述

#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>

using namespace cv;
int main(int argc, char** argv) {
	Mat src, dst;
	int ksize = 0;

	src = imread("C:/Users/Admin/Desktop/1.jpg");
	if (!src.data) {
		printf("could not load image...\n");
		return -1;
	}

	char INPUT_WIN[] = "input image";
	char OUTPUT_WIN[] = "Custom Blur Filter Result";
	namedWindow(INPUT_WIN, WINDOW_AUTOSIZE);
	namedWindow(OUTPUT_WIN, WINDOW_AUTOSIZE);

	imshow(INPUT_WIN, src);

	// Sobel X 方向
	Mat kernel_x = (Mat_<int>(3, 3) << -1, 0, 1, -2,0,2,-1,0,1);
	 filter2D(src, dst, -1, kernel_x, Point(-1, -1), 0.0);

	// Sobel Y 方向
	// Mat yimg;
	// Mat kernel_y = (Mat_<int>(3, 3) << -1, -2, -1, 0,0,0, 1,2,1);
	// filter2D(src, yimg, -1, kernel_y, Point(-1, -1), 0.0);

	// 拉普拉斯算子
	//Mat kernel_y = (Mat_<int>(3, 3) << 0, -1, 0, -1, 4, -1, 0, -1, 0);
	//filter2D(src, dst, -1, kernel_y, Point(-1, -1), 0.0);

imshow("OUTPUT_WIN", dst);
	 waitKey(0);
	return 0;
}

14.4自定义卷积模糊

filter2D方法filter2D(
Mat src, //输入图像
Mat dst, // 模糊图像
int depth, // 图像深度32/8
Mat kernel, // 卷积核/模板
Point anchor, // 锚点位置
double delta // 计算出来的像素+delta
)
其中 kernel是可以自定义的卷积核

在这里插入图片描述

#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>

using namespace cv;
int main(int argc, char** argv) {
	Mat src, dst;
	int ksize = 0;

	src = imread("D:/vcprojects/images/test1.png");
	if (!src.data) {
		printf("could not load image...\n");
		return -1;
	}

	char INPUT_WIN[] = "input image";
	char OUTPUT_WIN[] = "Custom Blur Filter Result";
	namedWindow(INPUT_WIN, CV_WINDOW_AUTOSIZE);
	namedWindow(OUTPUT_WIN, CV_WINDOW_AUTOSIZE);

	imshow(INPUT_WIN, src);

	int c = 0;
	int index = 0;
	while (true) {
		c = waitKey(500);
		if ((char)c == 27) {// ESC 
			break;
		}
		ksize = 5 + (index % 8) * 2;
		Mat kernel = Mat::ones(Size(ksize, ksize), CV_32F) / (float)(ksize * ksize);
		filter2D(src, dst, -1, kernel, Point(-1, -1));
		index++;
		imshow(OUTPUT_WIN, dst);	
	}

	
	return 0;
}

十五、处理边缘

15.1卷积边界问题

图像卷积的时候边界像素,不能被卷积操作,原因在于边界像素没有完全跟kernel重叠,所以当3x3滤波时候有1个像素的边缘没有被处理,5x5滤波的时候有2个像素的边缘没有被处理。
在这里插入图片描述

15.2处理边缘

在卷积开始之前增加边缘像素,填充的像素值为0或者RGB黑色,比如3x3在
四周各填充1个像素的边缘,这样就确保图像的边缘被处理,在卷积处理之
后再去掉这些边缘。openCV中默认的处理方法是: BORDER_DEFAULT,此外
常用的还有如下几种:

  • BORDER_CONSTANT – 填充边缘用指定像素值
  • BORDER_REPLICATE – 填充边缘像素用已知的边缘像素值。
  • BORDER_WRAP – 用另外一边的像素来补偿填充

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

15.3API说明 – 给图像添加边缘API

copyMakeBorder(
 - Mat src, // 输入图像
 - Mat dst, // 添加边缘图像
 - int top, // 边缘长度,一般上下左右都取相同值,
 - int bottom,
 - int left,
 - int right, 
 - int borderType // 边缘类型
 - Scalar value 
)


#include <opencv2/opencv.hpp>
#include <opencv2/imgproc.hpp>
#include<iostream>
#include<math.h>
using namespace std;
using namespace cv;
Mat src,dst;
char input_win[] = "input image";
char output_win[] = "output image";
int main(int argc, char** argv)
{    
	 src = imread("C:/Users/Admin/Desktop/1.jpg");	//读取图片(使用图片的绝对路径)	
	if (src.empty()) {
		printf("could not find the image!\n");
		return -1;
	}
	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);
	
	RNG rng(12345);//用于构造随机值
	int borderType = BORDER_DEFAULT;
	
	int c = 0;
	while (true) {
		c = waitKey(500);
		// ESC
		if ((char)c == 27) {
			break;
		}
		if ((char)c == 'r') {
			borderType = BORDER_REPLICATE;
		} else if((char)c == 'w') {
			borderType = BORDER_WRAP;
		} else if((char)c == 'c') {
			borderType = BORDER_CONSTANT;
		} 
		Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
		copyMakeBorder(src, dst, top, bottom, left, right, borderType, color);
		imshow(output_win, dst);
	}
	/*namedWindow(input_win, WINDOW_AUTOSIZE);
	);*/

	

	waitKey(0);//不加此语句图片会一闪而过
	system("path");
	getchar();
	return 0;
}



十六、Sobel算子

16.1卷积应用-图像边缘提取

边缘是什么 – 是像素值发生跃迁的地方,是图像的显著特征之一,在图像特征提取、对象检测、模式识别等方面都有重要的作用。
如何捕捉/提取边缘 – 对图像求它的一阶导数
delta = f(x) – f(x-1), delta越大,说明像素在X方向变化越大,边缘信号越强,
在这里插入图片描述

16.2Sobel算子

是离散微分算子(discrete differentiation operator),用来计算图像灰度的近似梯度
Soble算子功能集合高斯平滑和微分求导
又被称为一阶微分算子,求导算子,在水平和垂直两个方向上求导,得到图像X方法与Y方向梯度图像

在这里插入图片描述
求取导数的近似值,kernel=3时不是很准确,OpenCV使用改进版本Scharr函数,算子如下:
在这里插入图片描述

16.3API说明cv::Sobel

cv::Sobel (
InputArray Src // 输入图像
OutputArray dst// 输出图像,大小与输入图像一致
int depth // 输出图像深度.
Int dx. // X方向,几阶导数
int dy // Y方向,几阶导数.
int ksize, SOBEL算子kernel大小,必须是1、3、5、7、
double scale = 1
double delta = 0
int borderType = BORDER_DEFAULT
)
在这里插入图片描述



#include <opencv2/opencv.hpp>
#include <opencv2/imgproc.hpp>
#include<iostream>
#include<math.h>
using namespace std;
using namespace cv;
Mat src, dst;
char input_win[] = "input image";
char output_win[] = "output image";
int main(int argc, char** argv)
{
	src = imread("C:/Users/Admin/Desktop/1.jpg");	//读取图片(使用图片的绝对路径)	
	if (src.empty()) {
		printf("could not find the image!\n");
		return -1;
	}
	imshow(input_win, src);
	/*第一步:高斯模糊*/
	GaussianBlur(src, dst, Size(3, 3), 0, 0);
	//imshow(output_win, dst);
	/*第二步:转化为灰度图像*/
	Mat gray_src;
	cvtColor(dst, gray_src, COLOR_BGR2GRAY);
	//imshow("gray image", gray_src);
	/*第三步:求梯度x和y*/
	Mat xgrad, ygrad, xygrad;
	Sobel(gray_src, xgrad, CV_16S, 1, 0, 3);
	Sobel(gray_src, ygrad, CV_16S, 0, 1, 3);
	convertScaleAbs(xgrad, xgrad);//增强
	convertScaleAbs(ygrad, ygrad);
	addWeighted(xgrad, 0.5, ygrad, 0.5, 0, xygrad);
	imshow("xgrad image", xgrad);
	imshow("ygrad image", ygrad);
	imshow("xygrad image", xygrad);
	waitKey(0);//不加此语句图片会一闪而过
	system("path");
	getchar();
	return 0;
}



在这里插入图片描述

16.4API说明cv::Scharr

cv::Scharr (
InputArray Src // 输入图像
OutputArray dst// 输出图像,大小与输入图像一致
int depth // 输出图像深度.
Int dx. // X方向,几阶导数
int dy // Y方向,几阶导数.
double scale = 1
double delta = 0
int borderType = BORDER_DEFAULT
)






#include <opencv2/opencv.hpp>
#include <opencv2/imgproc.hpp>
#include<iostream>
#include<math.h>
using namespace std;
using namespace cv;
Mat src,dst;
char input_win[] = "input image";
char output_win[] = "output image";
int main(int argc, char** argv)
{    
	 src = imread("C:/Users/Admin/Desktop/1.jpg");	//读取图片(使用图片的绝对路径)	
	if (src.empty()) {
		printf("could not find the image!\n");
		return -1;
	}
	//imshow(input_win, src);
	/*第一步:高斯模糊*/
	GaussianBlur(src, dst, Size(3, 3), 0, 0);
	//imshow(output_win, dst);
	/*第二步:转化为灰度图像*/
	Mat gray_src;
	cvtColor(dst, gray_src, COLOR_BGR2GRAY);
	//imshow("gray image", gray_src);
	/*第三步:求梯度x和y*/
	Mat xgrad, ygrad;
	Scharr(gray_src, xgrad, CV_16S, 1, 0);//比Sobel更多细节
	Scharr(gray_src, ygrad, CV_16S, 0, 1);

	convertScaleAbs(xgrad, xgrad);
	convertScaleAbs(ygrad, ygrad);
	imshow("xgrad image", xgrad);
	imshow("ygrad image", ygrad);
	//增强
	Mat xygrad = Mat(xgrad.size(), xgrad.type());
	printf("type : %d\n", xgrad.type());
	int width = xgrad.cols;
	int height = ygrad.rows;
	for (int row = 0; row < height; row++) {
		for (int col = 0; col < width; col++) {
			int xg = xgrad.at<uchar>(row, col);
			int yg = ygrad.at<uchar>(row, col);
			int xy = xg + yg;
			xygrad.at<uchar>(row, col) = saturate_cast<uchar>(xy);
		}
	}
	imshow(output_win, xygrad);
	waitKey(0);//不加此语句图片会一闪而过
	system("path");
	getchar();
	return 0;
}








在这里插入图片描述

十七、Laplance算子

17.1Laplance算子

在这里插入图片描述
在这里插入图片描述
解释:在二阶导数的时候,最大变化处的值为零即边缘是零值。通过二阶
导数计算,依据此理论我们可以计算图像二阶导数,提取边缘。

17.2处理流程

高斯模糊 – 去噪声GaussianBlur()
转换为灰度图像cvtColor()
拉普拉斯 – 二阶导数计算Laplacian()
取绝对值convertScaleAbs()
显示结果



#include <opencv2/opencv.hpp>
#include <opencv2/imgproc.hpp>
#include<iostream>
#include<math.h>
using namespace std;
using namespace cv;
Mat src,dst;
char input_win[] = "input image";
char output_win[] = "output image";
int main(int argc, char** argv)
{    
	 src = imread("C:/Users/Admin/Desktop/1.jpg");	//读取图片(使用图片的绝对路径)	
	if (src.empty()) {
		printf("could not find the image!\n");
		return -1;
	}
	//imshow(input_win, src);
	/*第一步:高斯模糊*/
	GaussianBlur(src, dst, Size(3, 3), 0, 0);
	//imshow(output_win, dst);
	/*第二步:转化为灰度图像*/
	Mat gray_src;
	cvtColor(dst, gray_src, COLOR_BGR2GRAY);
	//imshow("gray image", gray_src);
	/*第三步:拉普拉斯 – 二阶导数计算Laplacian()*/
	Mat edge_image;
	Laplacian(gray_src, edge_image, CV_16S, 3);
	convertScaleAbs(edge_image, edge_image);//增强
	threshold(edge_image, edge_image, 0, 255, THRESH_OTSU | THRESH_BINARY);
	imshow("ygrad image", edge_image);
	
	waitKey(0);//不加此语句图片会一闪而过
	system("path");
	getchar();
	return 0;
}



在这里插入图片描述

17.3API使用cv::Laplacian

Laplacian(
InputArray src,
OutputArray dst,
int depth, //深度CV_16S
int kisze, // 3
double scale = 1,
double delta =0.0,
int borderType = 4
)

十八、Canny边缘检测

18.1Canny算法介绍

Canny是边缘检测算法,在1986年提出的。
是一个很好的边缘检测器
很常用也很实用的图像处理方法

Canny算法介绍 – 五步 in cv::Canny

高斯模糊 - GaussianBlur
灰度转换 - cvtColor
计算梯度 – Sobel/Scharr
非最大信号抑制
高低阈值输出二值图像

Canny算法介绍 - 非最大信号抑制

在这里插入图片描述

Canny算法介绍-高低阈值输出二值图像

T1, T2为阈值,凡是高于T2的都保留,凡是小于T1都丢弃,从高于T2的像素出发,凡是大于T1而且相互连接的,都保留。最终得到一个输出二值图像。
推荐的高低阈值比值为 T2: T1 = 3:1/2:1其中T2为高阈值,T1为低阈值

18.2API – cv::Canny

Canny(
InputArray src, // 8-bit的输入图像
OutputArray edges,// 输出边缘图像, 一般都是二值图像,背景是黑色
double threshold1,// 低阈值,常取高阈值的1/2或者1/3
double threshold2,// 高阈值
int aptertureSize,// Soble算子的size,通常3x3,取值3
bool L2gradient // 选择 true表示是L2来归一化,否则用L1归一化

在这里插入图片描述
在这里插入图片描述
默认情况一般选择是L1,参数设置为false

#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>

using namespace cv;
Mat src, gray_src, dst;
int t1_value = 50;
int max_value = 255;
const char* OUTPUT_TITLE = "Canny Result";
void Canny_Demo(int, void*);
int main(int argc, char** argv) {
	src = imread("C:/Users/Admin/Desktop/1.jpg");
	if (!src.data) {
		printf("could not load image...\n");
		return -1;
	}

	char INPUT_TITLE[] = "input image";
	namedWindow(INPUT_TITLE,WINDOW_AUTOSIZE);
	namedWindow(OUTPUT_TITLE,WINDOW_AUTOSIZE);
	imshow(INPUT_TITLE, src);

	cvtColor(src, gray_src, COLOR_BGR2GRAY);
	createTrackbar("Threshold Value:", OUTPUT_TITLE, &t1_value, max_value, Canny_Demo);
	Canny_Demo(0, 0);

	waitKey(0);
	return 0;
}

void Canny_Demo(int, void*) {
	Mat edge_output;
	blur(gray_src, gray_src, Size(3, 3), Point(-1, -1), BORDER_DEFAULT);
	Canny(gray_src, edge_output, t1_value, t1_value * 2, 3, false);

	//dst.create(src.size(), src.type());
	//src.copyTo(dst, edge_output);
	// (edge_output, edge_output);
	imshow(OUTPUT_TITLE, ~edge_output);
}

在这里插入图片描述

  • 4
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值