C#-opencvsharp-图像中的数字提取

news/2024/12/31 6:43:13/

C#-opencv-图像中数字提取

本人初学者,正在学习C#中的opencv操作,下述代码目的是通过图像识别对银行卡的卡号进行识别并提取,要求位置置于银行卡原图中卡号正上方;
此次学习过程中通过查询python中的轮廓排序算法,手写了一个简易算法,方能实现此次学习的目的,同时加深了解了matchtemplate与matchshapes的应用区别,希望大家在阅读期间发现的问题的,及时反馈,本人会加以修正并提升!!!
卡片图像(百度获取)
在这里插入图片描述数字模板图像(百度获取)
在这里插入图片描述

0.准备工作

using System;
using OpenCvSharp;
using OpenCvSharp.Extensions;
using Size = OpenCvSharp.Size;
using Point = OpenCvSharp.Point;

1.定义轮廓排序简易函数-contours_sort

排序原理简单如下:
由上到下-通过轮廓获取外边界矩形(x,y,width,height)-y坐标从小到大排序
由下到上-通过轮廓获取外边界矩形(x,y,width,height)-y坐标+height(矩形高度)从大到小排序
由左到右-通过轮廓获取外边界矩形(x,y,width,height)-x坐标从小到大排序
由右到左-通过轮廓获取外边界矩形(x,y,width,height)-x坐标+width(矩形宽度)从大到小排序
轮廓面积由小到大-cv2.contourarea获取轮廓面积
轮廓面积由大到小
轮廓周长由小到大-cv2.arclength获取轮廓周长
轮廓周长由大到小

//轮廓排序函数简易C#版static private Point[][] contours_sort(Point[][] cnts,string sortMethod){var contours = cnts;int cnt_num = contours.Length;Rect[] rects = new Rect[cnt_num];for(int i = 0; i < cnt_num; i++) rects[i] = Cv2.BoundingRect(contours[i]);string[] sortType = { "top-to-bottom",        //0-坐标由上到下排序"bottom-to-top",        //1-坐标由下到上排序"left-to-right",        //2-坐标由左到右排序"right-to-left",        //3-坐标由右到左排序"area-min-to-max",      //4-面积-从小到大"area-max-to-min",      //5-面积-从大到小"length-min-to-max",    //6-周长-从小到达"length-max-to-min"     //7周长从大到小};int id = -1;for(int i = 0; i < sortType.Length; i++){if (sortMethod == sortType[i]) id = i;}switch (id) {case 0:for(int i = 0; i < cnt_num; i++){for(int j = i+1; j < cnt_num; j++){if (rects[j].Y <= rects[i].Y){Rect tpr = rects[i];rects[i] = rects[j];rects[j] = tpr;var temp = contours[i];contours[i] = contours[j];contours[j] = temp;}}}break;case 1:for (int i = 0; i < cnt_num; i++){for (int j = i + 1; j < cnt_num; j++){if (rects[j].Y+ rects[j].Height >= rects[i].Y + rects[i].Height){Rect tpr = rects[i];rects[i] = rects[j];rects[j] = tpr;var temp = contours[i];contours[i] = contours[j];contours[j] = temp;}}}break;case 2:for (int i = 0; i < cnt_num; i++){for (int j = i + 1; j < cnt_num; j++){if (rects[j].X <= rects[i].X){Rect tpr = rects[i];rects[i] = rects[j];rects[j] = tpr;var temp = contours[i];contours[i] = contours[j];contours[j] = temp;}}}break;case 3:for (int i = 0; i < cnt_num; i++){for (int j = i + 1; j < cnt_num; j++){if (rects[j].X+rects[j].Width >= rects[i].X + rects[i].Width){Rect tpr = rects[i];rects[i] = rects[j];rects[j] = tpr;var temp = contours[i];contours[i] = contours[j];contours[j] = temp;}}}break;case 4:for (int i = 0; i < cnt_num; i++){for (int j = i + 1; j < cnt_num; j++){if (Cv2.ContourArea(contours[j])<= Cv2.ContourArea(contours[j])){var temp = contours[i];contours[i] = contours[j];contours[j] = temp;}}}break;case 5:for (int i = 0; i < cnt_num; i++){for (int j = i + 1; j < cnt_num; j++){if (Cv2.ContourArea(contours[j]) >= Cv2.ContourArea(contours[j])){var temp = contours[i];contours[i] = contours[j];contours[j] = temp;}}}break;case 6:for (int i = 0; i < cnt_num; i++){for (int j = i + 1; j < cnt_num; j++){if (Cv2.ArcLength(contours[j],false) <= Cv2.ArcLength(contours[j], false)){var temp = contours[i];contours[i] = contours[j];contours[j] = temp;}}}break;case 7:for (int i = 0; i < cnt_num; i++){for (int j = i + 1; j < cnt_num; j++){if (Cv2.ArcLength(contours[j], false) >= Cv2.ArcLength(contours[j], false)){var temp = contours[i];contours[i] = contours[j];contours[j] = temp;}}}break;}return contours;}

2.常规参数设置

		    //标准尺寸设定int stand_width = 57;int stand_height = 88;//图像路径string c_path = "D://Jay.Lee//Study//2023//imgs//card.png";string t_path = "D://Jay.Lee//Study//2023//imgs//number.png";//图像获取Mat card = Cv2.ImRead(c_path);Mat template = Cv2.ImRead(t_path);//图像显示Cv2.ImShow("card原图显示", card);Cv2.ImShow("数字模板显示", template);

3.数字模板处理

            //1.数字模板处理Mat tgray = new Mat();//1.1图像灰度处理Cv2.CvtColor(template, tgray, ColorConversionCodes.BGR2GRAY);//1.2图像二值化Cv2.Threshold(tgray, tgray, 50, 255, ThresholdTypes.BinaryInv);//1.3图像图像轮廓查找Cv2.FindContours(tgray.Clone(), out Point[][] tcnts, out HierarchyIndex[] hierarchy,RetrievalModes.External, ContourApproximationModes.ApproxSimple);//此处注意-后期依旧使用tgray图像,此处使用tgray.clone()//1.4轮廓从左向右排序tcnts = contours_sort(tcnts, "left-to-right");//1.5从二值化图中扣取每个存在外轮廓的矩形Mat[] templateImages = new Mat[tcnts.Length];for (int i = 0; i < tcnts.Length; i++){Rect r = Cv2.BoundingRect(tcnts[i]);templateImages[i] = new Mat(tgray, new Rect(r.X , r.Y , r.Width , r.Height ));Cv2.Resize(templateImages[i], templateImages[i], new Size(stand_width, stand_height),0,0);}

4.卡片图像预处理

            //2.卡片预处理//2.1形态学核定义var rowkernel = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(11, 3));var gapkernel = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(9, 9));Mat cgray = new Mat();//2.2图像灰度处理Cv2.CvtColor(card, cgray, ColorConversionCodes.BGR2GRAY);//2.3图像二值化(Binary和Otsu自动判断)Cv2.Threshold(cgray, cgray, 0, 255, ThresholdTypes.Binary | ThresholdTypes.Otsu);//2.4图像缩小,便于后期形态学处理Mat small = cgray.Clone();Cv2.Resize(small, small, new Size(0, 0), 0.5, 0.5);//2.5两次闭处理,保证数字全部提取Cv2.MorphologyEx(small, small, MorphTypes.Close, rowkernel);Cv2.MorphologyEx(small, small, MorphTypes.Close, gapkernel);//2.6再次二值化Cv2.Threshold(small, small, 2, 255, ThresholdTypes.Binary);//2.6二值化图像放大至原图像尺寸Cv2.Resize(small, small, new Size(0, 0), 2, 2);//2.7二值化图像中轮廓提取Cv2.FindContours(small.Clone(), out Point[][] ccnts, out HierarchyIndex[] chierarchy,RetrievalModes.External, ContourApproximationModes.ApproxSimple);//2.8查询待得知数字轮廓及特征并进行筛选,此处得知高度为30int select_parts = 0;for (int i = 0; i < ccnts.Length; i++){Rect rect = Cv2.BoundingRect(ccnts[i]);if (rect.Height == 30) select_parts += 1; }Point[][] sccnts = new Point[select_parts][];for(int i = 0; i < ccnts.Length; i++){Rect rect = Cv2.BoundingRect(ccnts[i]);if (rect.Height == 30)//筛选高度满足卡号数字特征的轮廓{for(int j = 0; j < select_parts; j++){if (sccnts[j] == null){sccnts[j] = ccnts[i];break;}}}}//2.9轮廓由左向右排序sccnts = contours_sort(sccnts, "left-to-right");Rect[] rects = new Rect[select_parts];//3.模板中数字查询并添加至原图for (int i = 0; i < select_parts; i++) rects[i] = Cv2.BoundingRect(sccnts[i]);//3.1对上述的每个轮廓切为仅存在一个外轮廓图像for(int i = 0; i < select_parts; i++){string numbers = null;//初始化待添加至图像的字符串Mat card_parts = new Mat(cgray, rects[i]));//初始化存在卡号的局部卡片图像Cv2.FindContours(card_parts.Clone(), out Point[][] partcnts, out HierarchyIndex[] part_hierarchy,RetrievalModes.External, ContourApproximationModes.ApproxSimple);//寻找全部外轮廓Cv2.Threshold(card_parts, card_parts, 0, 255, ThresholdTypes.Binary | ThresholdTypes.Otsu);//卡片局部图像二值化partcnts = contours_sort(partcnts, "left-to-right");Rect[] part_rects = new Rect[partcnts.Length];

5.模板数字与卡片扣取数字匹配

                //3.2扣取每个单个轮廓中的数字,并与模板匹配,添加至原图for(int j = 0; j < partcnts.Length; j++){part_rects[j] = Cv2.BoundingRect(partcnts[j]);Mat part = new Mat(card_parts, rects[j]);Cv2.Resize(part, part, new Size(stand_width, stand_height));//将扣取数字图像尺寸与模板数字图像尺寸相同double max_score = double.NegativeInfinity;//初始化每个图像的最佳成绩double max_id = -1;//初始化对应最佳模板的idfor(int k = 0; k < templateImages.Length; k++){Mat matchr = new Mat();Cv2.MatchTemplate(templateImages[k], part, matchr, TemplateMatchModes.CCoeff);//将图像与模板中每个图像进行模板匹配Cv2.MinMaxLoc(matchr, out double minval, out double score);//获取最佳成绩及相应idif (score >= max_score){max_score = score;max_id = k;}}numbers += max_id.ToString();//存储至字符串,以备后期添加}//将字符串添加至原图Cv2.PutText(card, numbers, new Point(rects[i].X, rects[i].Y - 20), HersheyFonts.HersheySimplex, 1, new Scalar(0, 0, 255), 2);}Cv2.ImShow("卡片数字显示", card);Cv2.WaitKey();Cv2.DestroyAllWindows();Cv2.WaitKey(0);}}

图像处理的最终结果
在这里插入图片描述

文章来源:https://blog.csdn.net/JAYLEE900/article/details/128810192
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ppmy.cn/news/289406.html

相关文章

图像增强锐化

分享下自己开发的关于扫描图像处理算法&#xff1a; &#xff08;1&#xff09;实现对彩色图像的增强锐化&#xff0c;去燥美化作用&#xff1b;类似扫描文档app的图像处理&#xff1b; &#xff08;2&#xff09;快速的图像处理&#xff0c; sdk部署不依赖任何第三方其他库&…

图像增强(拉普拉斯锐化增强)

图1为原图像&#xff0c;图2为拉普拉斯模板滤波器模板和带有对角项的公式的扩展模板进行处理以及锐化后的图像&#xff0c;图3为实践常用拉普拉斯模板1和模板2进行图像处理以及锐化后的图像。 对图像经过拉普拉斯滤波后&#xff0c;可以得到图像的边缘&#xff0c;再用原图减去…

什么是锐度?

锐度描述了照片中细节的清晰度&#xff0c;尽管清晰度最终受到相机设备、图像放大倍率和观看距离的限制。影响图像清晰度的两个基本因素是:分辨率和锐度。 锐度描述了图像信息在边缘处转换的速度&#xff0c;因此高锐度会导致锐化的过渡以及能够清晰定义边界的细节。 锐度功…

康耐视visionpro工具大全

本文已参与「新人创作礼」活动&#xff0c;一起开启掘金创作之路。 本文已参与「新人创作礼」活动&#xff0c;一起开启掘金创作之路。 本文已参与「新人创作礼」活动&#xff0c;一起开启掘金创作之路。 康耐视visionpro控件中文说明&#xff0c;方便大家以后使用康耐视的工具…

图像增强---锐化

USM代码&#xff1a; USM 锐化增强算法&#xff08;Unsharpen Mask&#xff09;&#xff0c;是图像卷积处理实现锐化常用的算法&#xff0c;这种锐化的方法就是对原图像先做一个高斯模糊&#xff0c;然后用原来的图像减去一个系数乘以高斯模糊之后的图像&#xff0c;然后再把值…

信度检验和效度检验

关于SPSS中的数据分析——信度效度检验 现阶段正处在毕业季阶段&#xff0c;很多同学可能都正在忙着去弄自己的论文和答辩很多时候我们在写论文答辩,甚至于其他课题研究的时候都会选择用问卷这种形式来收集数据。最后我们只需要针对问卷所取得的数据进行分析&#xff0c;就基本…

图像锐化算法Laplacian

目录 一、锐化定义二、Laplacian算法三、实验效果 一、锐化定义 图像锐化是使图像边缘更加清晰的一种图像处理方法。常用的做法是提取图像的高频分量&#xff0c;将其叠加到原图上。 图像高频分量的提取有两种做法:一种是用高通滤波器得到高频分量&#xff1b;另一种是用低通滤…

曲率(Curvature)

曲线的曲率 几何体的曲率对于不同的对象有不同的定义。首先来看最简单的平面曲线。 首先把曲线分成无穷小的小段&#xff0c;每一段看作某个圆的一小段圆弧。这个圆叫做“密切圆”&#xff08;Osculating Circle&#xff09;。由于它与曲线只相交于极小的一段&#xff0c;又称为…