c++ 将tiff图转成PNG图,裁白边,分图代码

应用场景

在光伏或半导体等领域,产线的缺陷检测能力至关重要,这大大影响了产品的良率,以及产线的生产效率。目前在目标检测领域,已经有了较为成熟的yolo目标检测算法,但是yolo对于小目标的检测能力是有限的,而工业中往往会遇到一些极其小的缺陷,小到只有三个像素的大小,而产线的相机在取图的过程中,往往会取到较大尺寸的原图,例如5500x5500尺寸的图,我们知道yolo的输入是固定的尺寸,比如640x640,我们能想到的第一个办法就是通过各种插值算法将我们的原图resize,但是考虑到缺陷在5500x5500的图上只占了三个像素,无论使用什么样的插值算法,resize之后的图像中的缺陷都会消失,这对于我们网络的训练效果是灾难性的,因此我们想到了通过裁图的方式,将我们的原图分图之后再训练,然后再推理端我们使用TensorRT的batch推理,提升大批量推理的速度,最后将一定数量的图拼接起来就可以实现极小缺陷的检测能力。

1.将tiff格式的图转成三通道的PNG图

产线中的工业相机和彩图软件有可能会将图像以tiff的格式保存,我们需要将tiff图转成RGB保存的PNG图或者JPG图:

void convert_tiff_png(std::vector<cv::String>in_filenames, cv::String out_filename)
{
    
    
	for (int i = 0; i < in_filenames.size(); i++)
	{
    
    
		vector<cv::Mat> Mat_list;
		bool is_empty = cv::imreadmulti(in_filenames[i], Mat_list);

		cv::Mat Image_DH = Mat_list[0];
		cv::Mat Image_R = Mat_list[1];
		cv::Mat Image_G = Mat_list[2];
		cv::Mat Image_B = Mat_list[3];

		int width = Image_DH.cols;
		int height = Image_DH.rows;

		cv::Mat result;
		cv::Mat imgs[3];
		imgs[2] = Image_R;
		imgs[1] = Image_G;
		imgs[0] = Image_B;

		cv::merge(imgs, 3, result);

		cv::imwrite(out_filename + to_string(i) + ".png", result);
		cout << "第" << i << "张tiff已转成png格式" << endl;
		/*	cv::namedWindow("result", WINDOW_NORMAL);
			cv::imshow("result", result);
			cv::waitKey(0);*/
	}
	cout << "全部转换完毕" << endl;
}

此时就可以将原本保存的tiff图像转成彩色的png图像了,如下图:
在这里插入图片描述
此时我们发现,我们需要裁剪的图像,由于采图方式会导致周边出现很多白边和不必要的目标,我们想要的只是中间的那一块光伏板。此时,我们需要下一步的定位以及裁剪。

2.裁PNG图的白边,只留下目标区域

这一部分通过使用传统图像算法来对中间的光伏片进行定位,使用的是阈值分割方法,首先找到光伏片的大致位置,输出一个二值的掩模,然后通过cv::boundingRect()来得到rect模板,然后通过rect模板对原图进行分割:

//定位光伏片
void segment_png(std::vector<cv::String>in_filenames, cv::String outfilename)
{
    
    
	for (int i = 0; i < in_filenames.size(); i++)
	{
    
    
		string image_path = in_filenames[i];
		cv::Mat image = cv::imread(image_path, 0);
		cv::Mat image1 = cv::imread(image_path);

		if (image.empty())
		{
    
    
			cout << "请确认图像路径是否正确" << endl;
		}

		cv::Mat image_1;
		//image.copyTo(image_1);

		pyrDown(image, image);
		pyrDown(image, image);
		pyrDown(image, image);
		cv::Mat image_binary;
		cv::threshold(image, image_binary, 0, 255, THRESH_BINARY_INV | THRESH_OTSU);
		Mat struct1, struct2, struct3;
		struct1 = getStructuringElement(1, Size(20, 50));
		cv::erode(image_binary, image_binary, struct1);
		struct2 = getStructuringElement(0, Size(20, 60));
		cv::dilate(image_binary, image_binary, struct2);
		vector<vector<Point>> contours;
		vector<Vec4i>hierarachy;
		//bitwise_not(Image_Binary, Image_Binary);
		cv::findContours(image_binary, contours, hierarachy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point());

		int size = contours.size();

		if (size > 0)
		{
    
    
			double area_max = 0;
			int index = 0;
			for (int i = 0; i < contours.size(); i++)
			{
    
    
				double area = cv::contourArea(contours[i]);
				if (area > area_max)
				{
    
    
					area_max = area;
					index = i;
				}
			}
			cv::Rect rect = cv::boundingRect(contours[index]);

			image_1 = image1(cv::Rect((rect.x) * 8, (rect.y) * 8, (rect.width) * 8, (rect.height) * 8));

			cv::imwrite(outfilename + to_string(i) + ".png", image_1);
			cout << "第" << i << "张png定位分割完毕" << endl;

			//cv::namedWindow("test", WINDOW_NORMAL);
			//cv::imshow("test", image_1);
			//cv::waitKey(0);
		}
	}
	cout << "全部定位分割完毕" << endl;
}

经过这一步之后,原图的白边已经得到了很大程度的裁剪:
在这里插入图片描述

3.对图像进行分图并保存

struct CeilPosition
{
    
    
	Mat ceil_image;
	int x;
	int y;
};

vector<CeilPosition> split_image(Mat src) {
    
    

	vector<CeilPosition>ceil_position;
	// resize(src, src, cv::Size(WIDTH, HEIGHT));
	int width = src.cols;
	int height = src.rows;

	OVERLAP_X = input_width - width/M;
	OVERLAP_Y = input_height - height / N;

	int SUB_WIDTH = width / M;
	int SUB_HEIGHT = height / N;

	for (int j = 0; j < N; j++)
	{
    
    
		for (int i = 0; i < M; i++)
		{
    
    
			if (i == 0 && j == 0) {
    
    

				Mat image_cut, roi_img;
				Rect rect(i * SUB_WIDTH, j * SUB_HEIGHT, SUB_WIDTH + OVERLAP_X, SUB_HEIGHT + OVERLAP_Y);
				image_cut = Mat(src, rect);
				roi_img = image_cut.clone();

				ceil_position.push_back({
    
     roi_img,i * SUB_WIDTH,j * SUB_HEIGHT });
			}
			else if (i == 0 && j != 0) {
    
    
				Mat image_cut, roi_img;
				Rect rect(i * SUB_WIDTH, j * SUB_HEIGHT - OVERLAP_Y, SUB_WIDTH + OVERLAP_X, SUB_HEIGHT + OVERLAP_Y);
				image_cut = Mat(src, rect);
				roi_img = image_cut.clone();
				ceil_position.push_back({
    
     roi_img,i * SUB_WIDTH, j * SUB_HEIGHT - OVERLAP_Y });
			}
			else if (i != 0 && j == 0) {
    
    
				Mat image_cut, roi_img;
				Rect rect(i * SUB_WIDTH - OVERLAP_X, j * SUB_HEIGHT, SUB_WIDTH + OVERLAP_X, SUB_HEIGHT + OVERLAP_Y);
				image_cut = Mat(src, rect);
				roi_img = image_cut.clone();
				ceil_position.push_back({
    
     roi_img,i * SUB_WIDTH - OVERLAP_X, j * SUB_HEIGHT });
			}
			else {
    
    
				Mat image_cut, roi_img;
				Rect rect(i * SUB_WIDTH - OVERLAP_X, j * SUB_HEIGHT - OVERLAP_Y, SUB_WIDTH + OVERLAP_X, SUB_HEIGHT + OVERLAP_Y);
				image_cut = Mat(src, rect);
				roi_img = image_cut.clone();
				ceil_position.push_back({
    
     roi_img,i * SUB_WIDTH - OVERLAP_X, j * SUB_HEIGHT - OVERLAP_Y });
			}
		}
	}
	return ceil_position;
}

主函数:

int main()
{
    
    
	std::vector<cv::String> filenames;
	cv::String folder = "";
	cv::String folder_ = "";
	cv::glob(folder, filenames);
	cv::String outfilename = "";
	cv::String outfilename_ = "";

	//1.将tiff图转成彩色png图保存
	//convert_tiff_png(filenames, outfilename);

	//2.将png图分割出来
	/*std::vector<cv::String> filenames_;
	cv::glob(folder_, filenames_);
	segment_png(filenames_, outfilename_);*/

	//3.将分割后的png图切成n个小块
	std::vector<CeilPosition> image_set;
	std::vector<cv::String> filenames_seg;
	cv::String folder_seg = "";
	cv::glob(folder_seg, filenames_seg);

	cv::Mat src = cv::imread(filenames_seg[1]);

	image_set = split_image(src);
	
	for (int k = 0; k < 22 * 22; k++)
	{
    
    
		cv::imwrite("", image_set[k].ceil_image);
		cout << "第" << k << "张分图保存好了" << endl;
	}

	return 0;
}

猜你喜欢

转载自blog.csdn.net/ycx_ccc/article/details/132274856