【学习C++知识点】

1. .h文件和.cpp文件并不是名字要一一对应(只是一般默认是这样写)

  1. .h文件用来声明函数,.cpp文件用来定义函数,调用函数(图1)
  2. 需要注意:cpp中的函数,前面需要写**类::**,来证明这是哪个类函数下面的(图2)
  3. C++中,函数中不能再写一个函数(图3)

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

2. cv::Rect rect是个矩形

含有属性:左上角的xy点坐标和宽高属性
在这里插入图片描述

2.1 可以直接在图片上找到矩形框出的位置

cv::Mat src,
cv::Mat roi = src(rect)

3. 进行操作时,cv::Mat相当于指针可以覆盖

cv::cvtColor(roi, roi, COLOR_RGB2GRAY);

4. 输入一个和输入一组是不一样的

说明:比如drawContours函数,需要输入的参数是一个vector容器,直接给他一个元素是不行的,需要把元素放在容器里再传进去
在这里插入图片描述

5. Vec3b 获取RGB三通道的值

  1. Vec3b 是一个包含三个无符号字符(8位)的结构体,通常用于表示 BGR(蓝绿红)颜色空间中的像素值
 result.at <Vec3b> (y, x)代表的是对 result 矩阵对象的访问操作。
 at 是一个成员函数,用于访问矩阵中指定位置的元素。在这里,y 和 x 分别表示行和列的索引
 ---访问的是该位置的RGB三个值
 
 cv::Mat MainWindow::px_subtract(cv::Mat src, cv::Mat temple, cv::Mat result)
	{
		for (int y = 0; y < src.rows; y++)	// ------------3----逐像素相减(用异常图-模版图)
		{
			for (int x = 0; x < src.cols; x++)
			{
				Vec3b pixel1 = src.at<Vec3b>(y, x);
				Vec3b pixel2 = temple.at<Vec3b>(y, x);
				result.at<Vec3b>(y, x) = Vec3b(abs(pixel1[0] - pixel2[0]), abs(pixel1[1] - pixel2[1]), abs(pixel1[2] - pixel2[2]));
			}
		}
		return result;
	}

5. 补充,读取单通道和三通道的像素值

5.1 单通道uchar存储,at.<cv::uchar>访问

5.2 三通道cv::Vec3b存储,at.<cv::Vec3b>访问

单通道灰度图
如果是一张单通道的灰度图像,你可以使用cv::Scalar来存储像素值,并使用uchar作为像素类型,at<uchar>读取像素值。
cv::Mat image = cv::imread("gray_example.jpg", cv::IMREAD_GRAYSCALE); // 读取灰度图像  
cv::Scalar pixel = image.at<uchar>(y, x); // 读取(x, y)位置的像素值  



RGB三通道灰度值
cv::Vec3b 是 OpenCV 库中的一个数据类型,用于表示一个3通道的字节型像素值。在计算机视觉和图像处理中,图像通常由像素组成,每个像素可以有多个通道,例如RGB通道。cv::Vec3b 就是一个用于存储RGB三个通道值的结构。
在cv::Vec3b中:
第一个通道(b)通常表示蓝色通道的强度。
第二个通道(g)通常表示绿色通道的强度。
第三个通道(r)通常表示红色通道的强度。
每个通道的值通常是一个字节(8位),范围从0255。


cv::Mat image = cv::imread("example.jpg"); // 读取图像  
cv::Vec3b pixel = image.at<cv::Vec3b>(y, x); // 读取(x, y)位置的像素值  
// 修改像素值  
pixel[0] = 255; // 蓝色通道  
pixel[1] = 255; // 绿色通道  
pixel[2] = 255; // 红色通道  
image.at<cv::Vec3b>(y, x) = pixel; // 将修改后的像素值写回图像


6. 轮廓代码

查找轮廓
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
findContours(binary, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
画轮廓的函数
cv::Mat result1 = Mat::zeros(binary.size(), binary.type());
drawContours(result1, new2_contours, -1, Scalar(255), 2);
imshow("去除外边缘10px绘制轮廓-4", result1);
找外接矩
void MainWindow::contours_Rectangle(vector<vector<cv::Point>> all_contours, vector<cv::Rect> &all_Rectangle) {
		for (size_t i = 0; i < all_contours.size(); i++) {
			Rect rect = boundingRect(all_contours[i]); //这个函数指的是这个轮廓的最小外接矩
			all_Rectangle.push_back(rect);
		}
	}
画矩形的函数cv::rectangle
cv::Mat rectangle_img = cv::Mat::zeros(edge.size(), CV_8UC3);  //能错在这个地方CV_8UC3,呜呜呜....
for (size_t i = 0; i < canny_rectangle.size(); i++) {
	cv::rectangle(rectangle_img, canny_rectangle[i], cv::Scalar(0, 255, 0), 2);  //画轮廓和画矩形的函数不一样
	}
	cv::imshow("Bounding Rectangles", rectangle_img);
	cv::waitKey(0);
上面找到的轮廓的坐标是基于小图的,而不是基于原图的,要想在原图上显示,需要加一个偏移量
for (int i = 0; i < new4_contours.size(); i++)
{
	for (int j = 0; j < new4_contours[i].size(); j++)
	{
		new4_contours[i][j].x = new4_contours[i][j].x + rect.x;
		new4_contours[i][j].y = new4_contours[i][j].y + rect.y;
	}
}

7. vs中打开控制台,使用cout<< X <<endl;打印参数

在这里插入图片描述

8. 开操作

//定义卷积核
cv::Size kernel1 = cv::Size(1, 1);
cv::Mat kernel_1 = cv::getStructuringElement(cv::MORPH_RECT, kernel1, cv::Point(-1, -1));

cv::Mat diff; 
cv::morphologyEx(srcImage_, diff, cv::MORPH_OPEN, kernel_1); // 对检测图开操作,先腐蚀后膨胀

9. 梯度幅值图

void MainWindow::grad_1(cv::Mat src, cv::Mat &grad_map) {


		if (src.empty())
		{
			cout << "无法读取图像" << endl;//改在日志中提示
		}
		// 创建输出图像
		Mat grad_x, grad_y, abs_grad_x, abs_grad_y;

		// 计算x方向梯度
		Sobel(src, grad_x, CV_16S, 1, 0, 3, 1, 0, BORDER_DEFAULT);
		// 计算y方向梯度
		Sobel(src, grad_y, CV_16S, 0, 1, 3, 1, 0, BORDER_DEFAULT);

		// 计算绝对值
		convertScaleAbs(grad_x, abs_grad_x);
		convertScaleAbs(grad_y, abs_grad_y);

		// 合并梯度
		addWeighted(abs_grad_x, 0.5, abs_grad_y, 0.5, 0, grad_map);

		// 显示结果
		/*mshow("原始图像", src);
		imshow("梯度幅值图", grad_map);

		waitKey(0);*/
		//return grad_map;
	

	}

10. 自己写的滑动窗口【矩形在原图上滑动】

void MainWindow::slide_window(cv::Mat src, cv::Rect rect, vector<cv::Mat> &vector_map)
	{
		int h = 0;// 制定循环的次数
		int w = 0;
		(src.rows % rect.height == 0) ? (h = src.rows / rect.height) : (h = src.rows / rect.height + 1);
		(src.cols % rect.width == 0) ? (w = src.cols / rect.width) : (w = src.cols / rect.width + 1);

		for (size_t i = 0; i < h; i++)
		{

			if(rect.y + rect.height > src.rows)  //先判断矩形高度有没有超过
			{  
				rect.height = src.rows - rect.y;
			}

			for (size_t j = 0; j < w; j++)
			{
				if (rect.x + rect.width > src.cols)  //判断矩形宽度有没有超过
				{
					rect.width = src.cols - rect.x;
				}
				cv::Mat temp = src(rect);
				vector_map.push_back(temp);

				rect.x = rect.x + rect.width;
			}
			rect.x = 0;
			rect.y = rect.y + rect.height;
		}

	}


10.1 补充,把原图分成X*X大小的块,分区域二值化,再贴到一张同尺寸图上


这个函数需要注意的是,dst是接收返回的二值化图像,所以引用这个函数钱,需要定义的dst是单通道的图

cv::Mat dst(src.size(), CV_8UC1); ******非常重要的知识点


void localThreshold(cv::Mat& src, cv::Mat& dst, int size)
{
	if (src.empty() || src.size() != dst.size() || size == 0)
	{
		ClassErrorLog("local threshold fail!");
		FlushLog();
		return;
	}
	cv::Mat subThreshImg;		//切片区域阈值结果
	cv::Rect subRect;			//切片区域范围

	int dW = src.cols / size;
	int dH = src.rows / size;
	int resW = src.cols - (size * dW);
	int resH = src.rows - (size * dH);
	for (int i = 0; i < size; i++)
	{
		for (int j = 0; j < size; j++)
		{
			subRect.x = j * dW;
			subRect.y = i * dH;
			subRect.width = dW;
			subRect.height = dH;
			if (j == (size - 1))
				subRect.width = dW + resW;
			if (i == (size - 1))
				subRect.height = dH + resH;
			int threshold = AlgorithmModule::ImageProcessAlg::customOtsu(src(subRect), 10);
			//这是先设置了一个低的阈值
			//threshold = threshold > curPaintPeelingDetectParams_.minThresh ? threshold : 255;
			cv::threshold(src(subRect), subThreshImg, threshold, 255, cv::THRESH_BINARY);
			subThreshImg.copyTo(dst(subRect));
		}
	}
	return;
}

11. release可以运行,debug不能运行时,试试

在这里插入图片描述

12. 遍历矩形时,容易越界,行列正确的代码函数

//rows 行数,代表高
//cols 列数,代表宽
for (size_t i = 0; i < image_gray.rows; i++) 
{
	for (size_t j = 0; j < image_gray.cols; j++)
	{
		uchar pixel = image_gray.at<uchar>(i, j);
	}
}

12.1 找灰度图像素值中位数代码

cvtColor(srcImage_, image_gray, COLOR_BGR2GRAY);//------------2----检测图像转换为灰度图像

vector<uchar> pixel_value;
for (size_t i = 0; i < image_gray.rows; i++)
{
	for (size_t j = 0; j < image_gray.cols; j++)
	{
		uchar pixel = image_gray.at<uchar>(i, j);
		pixel_value.push_back(pixel);
	}
}
// 对vector进行排序  
std::sort(pixel_value.begin(), pixel_value.end());

//计算中位数
int n = pixel_value.size();
cout << "总数为:" << n << endl;
int median = (int)pixel_value[n / 2];
std::cout << "中位数: " << median << std::endl;
cv::Mat srcImage_ = cv::imread("./Solution/HSV/1212/foreign-5/foreign_src-S/foreign_src-S.jpg");    // 这是输入的检测图
cv::Mat templateImage_ = cv::imread("./Solution/1212/foreign-1/foreign_template.jpg");  //这是模板图
cv::Mat image_gray;
cvtColor(srcImage_, image_gray, COLOR_BGR2GRAY);

vector<uchar> pixel_value;
for (size_t i = 0; i < image_gray.rows; i++)
{
	for (size_t j = 0; j < image_gray.cols; j++)
	{
		uchar pixel = image_gray.at<uchar>(i, j);
		pixel_value.push_back(pixel);
	}
}
// 对vector进行排序  
std::sort(pixel_value.begin(), pixel_value.end());

//计算中位数
int n = pixel_value.size();
cout << "总数为:" << n << endl;
int median = (int)pixel_value[n / 2];
std::cout << "中位数: " << median << std::endl;

cv::Mat binary;
threshold(image_gray, binary, median+20, 255, THRESH_BINARY_INV);
imshow("binary", binary);
waitKey(0);

13. 两个矩形交集和并集的操作

cv::Rect rect1;
cv::Rect rect2;
如果两个矩形相交,怎么判断?取交集,看看面积是不是大于0
cv::Rect jiaoji = rect1 & rect2;
if jiaoji.area > 0;就代表是有相交区域;

取两个矩形的并集
cv::Rect bingji = rect1 | rect2;

14. 重写大津法,图片中低于设定阈值的区域不参与运算

大津法的核心思想是在灰度直方图中寻找一个最佳的阈值,将图像分为两个类别使得类别内的差异最小,类别间的差异最大

//bgVal是设置的阈值,低于阈值的都不统计

int ImageProcessAlg::customOtsu(cv::Mat& src, uchar bgVal)
	{
		int threshold = 0;
		if (src.empty())
			return 0;

		double srcPixNum = 0;	//需统计的像素数量
		const int Grayscale = 256;	//灰度级数
		int graynum[Grayscale] = { 0 };	//各灰度级像素数量
		int height = src.rows;
		int width = src.cols;
		if (src.isContinuous())
		{
			width *= height;
			height = 1;
		}
		for (int i = 0; i < height; ++i)
		{
			const uchar* ptr = src.ptr<uchar>(i);
			for (int j = 0; j < width; ++j)
			{	//直方图统计
				if (ptr[j] > bgVal)	//剔除灰度(背景)
				{
					graynum[ptr[j]]++;
					++srcPixNum;
				}
			}
		}

		double P[Grayscale] = { 0 };
		double PK[Grayscale] = { 0 };
		double MK[Grayscale] = { 0 };
		double sumTmpPK = 0, sumTmpMK = 0;
		for (int i = 0; i < Grayscale; ++i)
		{
			P[i] = graynum[i] / srcPixNum;   //每个灰度级出现的概率
			PK[i] = sumTmpPK + P[i];         //概率累计和 
			sumTmpPK = PK[i];
			MK[i] = sumTmpMK + i*P[i];       //灰度级的累加均值                                                                                                                                                                                                                                                                                                                                                                                                        
			sumTmpMK = MK[i];
		}

		//计算类间方差
		double Var = 0;
		for (int k = 0; k < Grayscale; ++k)
		{
			double variance = (MK[Grayscale - 1] * PK[k] - MK[k])*(MK[Grayscale - 1] * PK[k] - MK[k]) / (PK[k] * (1 - PK[k]));
			if (variance > Var)
			{
				Var = variance;
				threshold = k;
			}
		}

		return threshold;
	}

  • 8
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值