栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Python

OpenCV小项目--识别银行卡轮廓

Python 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

OpenCV小项目--识别银行卡轮廓

文章目录

一、项目介绍二、项目分析

1.读取源图并设置为合适大小2.灰度处理3.高斯滤波4.边缘提取5.设置提取图像的轮廓大小并绘制6.提取图像 三、完整源码


一、项目介绍

本项目是识别银行卡的轮廓,是一个非常适合新手入门的小项目,笔者目前在自学OpenCV,使用的是OpenCV4.5.5+VS2017。
本项目的源码在最下面,不愿意看长篇大论的读者可直接跳到最下面,如对本项目有疑问欢迎留言讨论!

二、项目分析 1.读取源图并设置为合适大小

通过imread函数读取图片。
这里说一下,建议不要使用using namespace cv等,防止不同域名下存在相同的函数导致冲突,编译器无法识别。

//read image
cv::Mat srcImg = cv::imread("..\img\bank.png");
cv::Mat resImg;
if (srcImg.empty()) {
	std::cout << "Can not read image......." << std::endl;
	return -1;
}

下面这一行是针对源图尺寸不合适使用的,可以自定义图片的尺寸。

cv::resize(srcImg, srcImg, cv::Size(400, 250));

2.灰度处理

灰度处理就是把源图变为灰度图,方便尽心后续的边缘提取等操作。

cv::cvtColor(srcImg, grayImg, cv::COLOR_BGR2GRAY);

这里说一下为什么要进行灰度处理。
图像灰度化的目的是为了简化矩阵,提高运算速度。
彩色图片的每个像素的颜色由红绿蓝三分量(即常见的RGB)决定,每个分量的取值范围在0~255之间,对计算机来说,彩色图像的一个像素点就会有256256256=16777216种颜色的变化范围。
而灰度图是RGB分量相同的一种特殊彩色图像,对于计算机来说,灰度图的一个像素点的变化范围只有0~255这256种。
显而易见,彩色图的信息量过大,在进行图像处理时,灰度图的信息就足够使用了,因此进行灰度处理,可以提高运算速度。

3.高斯滤波
cv::GaussianBlur(grayImg, resImg, cv::Size(3, 3), 0.5, 0.5, 4);

网上对高斯滤波的介绍:
高斯滤波器是一种线性滤波器,能够有效的抑制噪声,平滑图像。 其作用原理和均值滤波器类似,都是取滤波器窗口内的像素的均值作为输出。 其窗口模板的系数和均值滤波器不同,均值滤波器的模板系数都是相同的为1;而高斯滤波器的模板系数,则随着距离模板中心的增大而系数减小。 所以,高斯滤波器相比于均值滤波器对图像个模糊程度较小。
这里就不做深入地探讨了,网上的资料也非常详细,这里只需要知道高斯滤波是用来平滑图像,消除噪声点即可。

4.边缘提取
//custom kernel size
cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5), cv::Point(-1, -1));
//closed operation
cv::morphologyEx(resImg, resImg, cv::MORPH_CLOSE, element);
//canny edge extraction
cv::Canny(resImg, resImg, 30, 90);

第一行是自定义形态学操作中的卷积核的尺寸。
在形态学上,核通常称为“构造元素”,cv::getStructuringElement()函数可以实现自定义形态学核。
该函数有三个参数:
int shape
cv::Size ksize
cv::Point anchor = cv::Point(-1,-1)
第一个参数shape控制构造元素的基本形状;后面两个控制的是元素的大小和锚点位置。通常,anchor默认为cv::Point(-1,-1),这表示默认锚点在元素中心。下图为元素形状种类。

形状值元素
cv::MORPH_RECT矩形
cv::MORPH_ELLIPSE椭圆形
cv::MORPH_CROSS交叉

第二行是闭操作。
首先明确几个概念:

    膨胀:膨胀是一种卷积操作,将目标像素的值替换成卷积和覆盖区域的局部最大值。腐蚀:与膨胀相反,将目标像素的值替换成卷积和覆盖区域的局部最小值。开操作:先将图像进行膨胀,之后对膨胀结果进行腐蚀。闭操作:与开操作相反,先腐蚀后膨胀。

膨胀和腐蚀应用在消除噪声、元素分割和连接等领域。
开操作消除的是大于邻域内点的孤立异常值,闭操作则是消除小于邻域内点的孤立异常值。
这里进行开操作便是消除图片中的白色斑点等。

第三行是canny算子进行边缘提取。
Canny算子由John F. Canny于 1986 年开发出来的一个多级边缘检测算法。
Canny 的目标是找到一个最优的边缘检测算法,最优边缘检测的含义是:
(1)最优检测:算法能够尽可能多地标识出图像中的实际边缘,漏检真实边缘的概率和误检非边缘的概率都尽可能小;
(2)最优定位准则:检测到的边缘点的位置距离实际边缘点的位置最近,或者是由于噪声影响引起检测出的边缘偏离物体的真实边缘的程度最小;
(3)检测点与边缘点一一对应:算子检测的边缘点与实际边缘点应该是一一对应。
为了满足这些要求 Canny 使用了变分法(calculus of variations),这是一种寻找优化特定功能的函数的方法。最优检测使用四个指数函数项表示,但是它非常近似于高斯函数的一阶导数。

//OpenCV中Canny函数中各参数含义
void cv::Canny	(	InputArray 	image,
					OutputArray 	edges,
					double 	threshold1,
					double 	threshold2,
					int 	apertureSize = 3,
					bool 	L2gradient = false)


threshold1和threshold2 当中的小阈值用来控制边缘连接,大的阈值用来控制强边缘的初始分割。
感兴趣的可以去这个链接详细看一下,这里就不细说了。

边缘提取的效果:

5.设置提取图像的轮廓大小并绘制

这里的话注释也写的比较详细了,大概的步骤就是通过轮廓查找统计轮廓的数量,之后设置阈值来进行轮廓筛选,最终得到期望的轮廓(绿色轮廓)。

	//define contour points and find contours
	std::vector> contours;
	std::vector hierarchy;
	
	//find contours
	cv::findContours(resImg, contours, hierarchy, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE);
	//print Number of contours
	std::cout << "Number of contours:" << " " << contours.size() << std::endl;

	//Define contours min width and min high
	int minWidth = resImg.cols*0.5;
	int minHeigh = resImg.rows*0.5;

	//Define the four vertices of the rectangle
	cv::RotatedRect minRect;
	cv::Point2f vertices[4];
	for (size_t t = 0; t < contours.size(); t++) {
		//find min rectangle
		minRect = cv::minAreaRect(contours[t]);
		//get slop angle
		double degree = std::abs(minRect.angle);
		//get data when the min rectangle is large than the min width and height 
		if (minRect.size.width > minWidth && minRect.size.height > minHeigh) {
			std::cout << "current rect:" << " " << t << std::endl;
			std::cout << "current angle:" << " " << degree << std::endl;
			//draw red contour sample on the source image
			srcImg.copyTo(grayImg);
			cv::drawContours(srcImg, contours, t, cv::Scalar(0, 0, 255), 1, 8, hierarchy, 0);
			//draw green rectangle on the source image
			minRect.points(vertices);
			for (int i = 0; i < 4; i++) {
				cv::line(srcImg, vertices[i], vertices[(i + 1) % 4], cv::Scalar(0, 255, 0), 4);
			}
			break;
		}
	}


红色的为采样轮廓,绿色为期望轮廓。

6.提取图像
//crop the image according to the min rectangle obtained
cv::Rect rect = minRect.boundingRect();

最终的效果图:

这样就把一张银行卡在一个图片中提取出来了。


三、完整源码
#include   
#include s
#include   
#include 


int main(int argc, char** argv)
{
	//read image
	cv::Mat srcImg = cv::imread("..\img\bank.png");
	cv::Mat resImg;
	if (srcImg.empty()) {
		std::cout << "Can not read image......." << std::endl;
		return -1;
	}

	//resize image
	cv::resize(srcImg, srcImg, cv::Size(400, 250));
	//show resized srcImg
	//cv::imshow("srcimg", srcImg);

	//change source image to gray image
	cv::Mat grayImg;
	cv::cvtColor(srcImg, grayImg, cv::COLOR_BGR2GRAY);
	//cv::imshow("grayImg", grayImg);

	//Gauss blur
	cv::GaussianBlur(grayImg, resImg, cv::Size(3, 3), 0.5, 0.5, 4);
	
	//custom kernel size
	cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5), cv::Point(-1, -1));
	//closed operation
	cv::morphologyEx(resImg, resImg, cv::MORPH_CLOSE, element);
	//canny edge extraction
	cv::Canny(resImg, resImg, 30, 90);
	//cv::imshow("resImg", resImg);

	//define contour points and find contours
	std::vector> contours;
	std::vector hierarchy;
	
	//find contours
	cv::findContours(resImg, contours, hierarchy, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE);
	//print Number of contours
	std::cout << "Number of contours:" << " " << contours.size() << std::endl;

	//Define contours min width and min high
	int minWidth = resImg.cols*0.5;
	int minHeigh = resImg.rows*0.5;

	//Define the four vertices of the rectangle
	cv::RotatedRect minRect;
	cv::Point2f vertices[4];
	for (size_t t = 0; t < contours.size(); t++) {
		//find min rectangle
		minRect = cv::minAreaRect(contours[t]);
		//get slop angle
		double degree = std::abs(minRect.angle);
		//get data when the min rectangle is large than the min width and height 
		if (minRect.size.width > minWidth && minRect.size.height > minHeigh) {
			std::cout << "current rect:" << " " << t << std::endl;
			std::cout << "current angle:" << " " << degree << std::endl;
			//draw red contour sample on the source image
			srcImg.copyTo(grayImg);
			cv::drawContours(srcImg, contours, t, cv::Scalar(0, 0, 255), 1, 8, hierarchy, 0);
			//draw green rectangle on the source image
			minRect.points(vertices);
			for (int i = 0; i < 4; i++) {
				cv::line(srcImg, vertices[i], vertices[(i + 1) % 4], cv::Scalar(0, 255, 0), 4);
			}
			break;
		}
	}
	//cv::imshow("srcImg", srcImg);

	//crop the image according to the min rectangle obtained
	cv::Rect rect = minRect.boundingRect();
	resImg = grayImg(rect);
	cv::imshow("res", resImg);
	cv::waitKey(0);
	cv::destroyAllWindows();
	return 0;
}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/714086.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号