图片找茬游戏——使用OpenCV查找两张图像的不同处并标记

前言

  • 有个小游戏,就是给出两张只有轻微的几处差异的图像,让大家来找出图像中的不同之处,之前曾经玩过,发现游戏明明告诉你有几处不同,但你什么也找不全,比如下面的几组图像,这几组图像每组都有三个不同的地方,可以试试能找出几处。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    不管你再什么认真找,也不容易找全其中差异的地方。以前试过从左到一块块的用眼睛扫描,但也是很容易漏掉细微的地方。可能是人的视觉感受野的问题吧。

2.在数字图像处理里面,每张图像都被看成一个三维的矩阵,那么把两张图像矩阵一相减,就得到差异的地方,在OpenCV库里面有封装的函数subtract()可以实现为个功能。这个API也可用来做差分法运动跟踪。
3.我这里的编程环境是Windows 10 64位,IDE是VS2019,配置了OpenCV 4.5,实现语言是C++。

二.代码实现

1.创建新的工程,把编译好的OpenCV导入到Vs里,添加系统环境。
2.实现代码

#include <opencv2/opencv.hpp>
#include <string>
#include <iostream>


void imshow(std::string name, const cv::Mat& cv_src)
{
	cv::namedWindow(name, 0);
	int max_rows = 800;
	int max_cols = 800;
	if (cv_src.rows >= cv_src.cols && cv_src.rows > max_rows)
	{
		cv::resizeWindow(name, cv::Size(cv_src.cols * max_rows / cv_src.rows, max_rows));
	}
	else if (cv_src.cols >= cv_src.rows && cv_src.cols > max_cols)
	{
		cv::resizeWindow(name, cv::Size(max_cols, cv_src.rows * max_cols / cv_src.cols));
	}
	cv::imshow(name, cv_src);
}

void imageSubtract(const cv::Mat& image1, const cv::Mat& image2,cv::Mat &cv_dst)
{
	if (image1.empty() || image2.empty())
	{
		return;
	}
	
	cv::Mat cv_src1 = image1.clone();
	cv::Mat cv_src2 = image2.clone();

	if ((image1.rows != image2.rows) || (image1.cols != image2.cols))
	{
		int rows = (image1.rows + image2.rows) / 2;
		int cols = (image1.cols + image2.cols) / 2;
		
		cv::resize(image1, cv_src1, cv::Size(cols, rows));
		cv::resize(image2, cv_src2, cv::Size(cols, rows));
	}

	cv::Mat image1_gary, image2_gary;

	if (cv_src1.channels() != 1)
	{
		cvtColor(cv_src1, image1_gary, cv::COLOR_BGR2GRAY);
	}
	if (cv_src2.channels() != 1)
	{
		cvtColor(cv_src2, image2_gary, cv::COLOR_BGR2GRAY);
	}

	cv::Mat frameDifference, absFrameDifferece;
	cv::Mat previousGrayFrame = image2_gary.clone();

	//图1减图2
	subtract(image1_gary, image2_gary, frameDifference, cv::Mat(), CV_16SC1);

	//取绝对值
	absFrameDifferece = abs(frameDifference);

	//位深的改变
	absFrameDifferece.convertTo(absFrameDifferece, CV_8UC1, 1, 0);
	imshow("absFrameDifferece", absFrameDifferece);
	
	cv::Mat segmentation;
	//阈值处理(这一步很关键,要调好二值化的值)
	threshold(absFrameDifferece, segmentation, 10, 255, cv::THRESH_BINARY);
	//threshold(absFrameDifferece, segmentation, 0, 255, cv::THRESH_OTSU);

	imshow("bin", segmentation);

	//形态学处理(开闭运算)
	//形态学处理用到的算子
	cv::Mat morphologyKernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5), cv::Point(-1, -1));
	morphologyEx(segmentation, segmentation, cv::MORPH_CLOSE, morphologyKernel, cv::Point(-1, -1), 2, cv::BORDER_REPLICATE);

	//找边界
	std::vector< std::vector<cv::Point> > contours;
	std::vector<cv::Vec4i> hierarchy;
	findContours(segmentation, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE, cv::Point(0, 0));//CV_RETR_TREE
	std::vector< std::vector<cv::Point> > contours_poly(contours.size());

	std::vector<cv::Rect> boundRect;

	cv_dst = cv_src1.clone();
	for (int index = 0; index < contours.size(); index++)
	{
		approxPolyDP(cv::Mat(contours[index]), contours_poly[index], 3, true);
		cv::Rect rect = cv::boundingRect(cv::Mat(contours_poly[index]));
		rectangle(cv_dst, rect, cv::Scalar(0, 0, 255), 2);
	}
}

int main()
{
	//读入图像
	cv::Mat cv_image1 = cv::imread("images/F1.jpg");
	cv::Mat cv_image2 = cv::imread("images/F2.jpg");

	cv::Mat cv_dst;
	//比较图像
	imageSubtract(cv_image1, cv_image2, cv_dst);
	imshow("A1", cv_image1);
	imshow("A2", cv_image2);
	imshow("dst", cv_dst);
	cv::imwrite("cv_dst.jpg", cv_dst);

	cv::waitKey();
	return 0;
}

3.运行效果:
在这里插入图片描述

3.结果对比:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
4.整个工程源码以上传到CSDN:https://download.csdn.net/download/matt45m/49898214

学习交流可加企鹅群:767133823

  • 10
    点赞
  • 67
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
使用C#和OpenCV来对两张图像的差异并标它们的位置,你可以使用以下步骤操作: 1. 引入必要的命名间: ```csharp using OpenCvSharp; using OpenCvSharp.Extensions; ``` 2. 加载两个输入图像: ```csharp Mat image1 = Cv2.ImRead("image1.jpg", ImreadModes.Color); Mat image2 = Cv2.ImRead("image2.jpg", ImreadModes.Color); ``` 3. 将图像转换为灰度图像: ```csharp Mat gray1 = new Mat(); Mat gray2 = new Mat(); Cv2.CvtColor(image1, gray1, ColorConversionCodes.BGR2GRAY); Cv2.CvtColor(image2, gray2, ColorConversionCodes.BGR2GRAY); ``` 4. 计算两个灰度图像的差异: ```csharp Mat diff = new Mat(); Cv2.Absdiff(gray1, gray2, diff); ``` 5. 对差异图像进行二值化理: ```csharp Mat threshold = new Mat(); Cv2.Threshold(diff, threshold, 30, 255, ThresholdTypes.Binary); ``` 6. 查找差异区域的轮廓: ```csharp Point[][] contours; HierarchyIndex[] hierarchy; Cv2.FindContours(threshold, out contours, out hierarchy, RetrievalModes.External, ContourApproximationModes.ApproxSimple); ``` 7. 标记差异区域的位置: ```csharp foreach (var contour in contours) { var rect = Cv2.BoundingRect(contour); Cv2.Rectangle(image1, rect, Scalar.Red, 2); } ``` 8. 显示带有标记图像: ```csharp Cv2.ImShow("Marked Image", image1); Cv2.WaitKey(0); ``` 9. 释放图像资源: ```csharp image1.Dispose(); image2.Dispose(); gray1.Dispose(); gray2.Dispose(); diff.Dispose(); threshold.Dispose(); ``` 上述代码将会对比两个输入图像并在第一个图像标记出差异的位置。你可以根据自己的需求调整标记的颜色、线条粗细等参数。 希望这个示例对你有帮助!如果还有其他问题,请随时提问。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

知来者逆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值