c#OpenCVSharp+Zxing识别条形码
参考博客:https://www.cnblogs.com/dengxiaojun/p/5278679.html,但是他的demo下载太贵了
可以下载这个https://download.csdn.net/download/dsq235612/10830805?utm_source=bbsseo,其实代码都差不多,目前只能识别简单的结构的图片
先添加引用,在nuget中添加OpenCVSharp类库和识别条码类库zxing
封装OpenCVSharp的调用代码:
public class OpencvHelper
{
/// <summary>
/// 灰度图
/// </summary>
/// <param name="srcImage">未处理的mat容器</param>
/// <param name="grayImage">灰度图mat容器</param>
public static void CvGrayImage(Mat srcImage, Mat grayImage)
{
if (srcImage.Channels() == )
{
Cv2.CvtColor(srcImage, grayImage, ColorConversionCodes.BGR2GRAY);
}
else
{
grayImage = srcImage.Clone();
}
//Imshow("灰度图", grayImage);
}
/// <summary>
/// 图像的梯度幅值
/// </summary>
/// <param name="grayImage"></param>
public static void CvConvertScaleAbs(Mat grayImage, Mat gradientImage)
{
//建立图像的梯度幅值
Mat gradientXImage = new Mat();
Mat gradientYImage = new Mat();
Cv2.Sobel(grayImage, gradientXImage, MatType.CV_32F, xorder: , yorder: , ksize: -);
Cv2.Sobel(grayImage, gradientYImage, MatType.CV_32F, xorder: , yorder: , ksize: -);
//Cv2.Scharr(grayImage, gradientXImage, MatType.CV_32F, 1, 0);//CV_16S CV_32F
//Cv2.Scharr(grayImage, gradientYImage, MatType.CV_32F, 0, 1);
//因为我们需要的条形码在需要X方向水平,所以更多的关注X方向的梯度幅值,而省略掉Y方向的梯度幅值
Cv2.Subtract(gradientXImage, gradientYImage, gradientImage);
//归一化为八位图像
Cv2.ConvertScaleAbs(gradientImage, gradientImage);
//看看得到的梯度图像是什么样子
//Imshow("图像的梯度幅值", gradientImage);
}
/// <summary>
/// 二值化图像
/// </summary>
public static void BlurImage(Mat gradientImage, Mat blurImage, Mat thresholdImage)
{
//对图片进行相应的模糊化,使一些噪点消除
//new OpenCvSharp.Size(12, 12); (9,9)
Cv2.Blur(gradientImage, blurImage, new OpenCvSharp.Size(, ));
//Cv2.GaussianBlur(gradientImage, blurImage, new OpenCvSharp.Size(7, 7), 0);//Size必须是奇数
//模糊化以后进行阈值化,得到到对应的黑白二值化图像,二值化的阈值可以根据实际情况调整
Cv2.Threshold(blurImage, thresholdImage, , , ThresholdTypes.Binary);
//看看二值化图像
//Imshow("二值化图像", thresholdImage);
}
/// <summary>
/// 闭运算
/// </summary>
public static void MorphImage(Mat thresholdImage, Mat morphImage)
{
//二值化以后的图像,条形码之间的黑白没有连接起来,就要进行形态学运算,消除缝隙,相当于小型的黑洞,选择闭运算
//因为是长条之间的缝隙,所以需要选择宽度大于长度
Mat kernel = Cv2.GetStructuringElement(MorphShapes.Rect, new OpenCvSharp.Size(, ));
Cv2.MorphologyEx(thresholdImage, morphImage, MorphTypes.Close, kernel);
//看看形态学操作以后的图像
//Imshow("闭运算", morphImage);
}
/// <summary>
/// 膨胀腐蚀
/// </summary>
public static void DilationErosionImage(Mat morphImage)
{
//现在要让条形码区域连接在一起,所以选择膨胀腐蚀,而且为了保持图形大小基本不变,应该使用相同次数的膨胀腐蚀
//先腐蚀,让其他区域的亮的地方变少最好是消除,然后膨胀回来,消除干扰,迭代次数根据实际情况选择
OpenCvSharp.Size size = new OpenCvSharp.Size(, );
OpenCvSharp.Point point = new OpenCvSharp.Point(-, -);
Cv2.Erode(morphImage, morphImage, Cv2.GetStructuringElement(MorphShapes.Rect, size), point, );
Cv2.Dilate(morphImage, morphImage, Cv2.GetStructuringElement(MorphShapes.Rect, size), point, );
//看看形态学操作以后的图像
//Imshow("膨胀腐蚀", morphImage);
}
/// <summary>
/// 显示处理后的图片
/// </summary>
/// <param name="name">处理过程名称</param>
/// <param name="srcImage">图片盒子</param>
public static void Imshow(string name, Mat srcImage)
{
using (var window = new Window(name, image: srcImage, flags: WindowMode.AutoSize))
{
Cv2.WaitKey();
}
//Cv2.ImShow(name, srcImage);
//Cv2.WaitKey(0);
}
/// <summary>
/// 旋转图片
/// </summary>
public static void RotateImage(Mat src, Mat dst, double angle, double scale)
{
var imageCenter = new Point2f(src.Cols / 2f, src.Rows / 2f);
var rotationMat = Cv2.GetRotationMatrix2D(imageCenter, angle, scale);
Cv2.WarpAffine(src, dst, rotationMat, src.Size());
}
}
调用封装的OpenCVSharp类的方法
/// <summary>
/// 读取图片
/// </summary>
private void DiscernImage()
{
string filename = FileHelper.OpenImageFile();
if (string.IsNullOrEmpty(filename)) return;
Image image = Image.FromFile(filename);
picImage.Image = image;
_imageFilePath = filename;
} private void OpenCV()
{
if (string.IsNullOrEmpty(_imageFilePath)) return;
Mat srcImage = new Mat(_imageFilePath, ImreadModes.Color);
if (srcImage.Empty()) { return; } //图像转换为灰度图像
Mat grayImage = new Mat();
OpencvHelper.CvGrayImage(srcImage, grayImage);
ShowImage("灰度图像", grayImage); //OpencvHelper.RotateImage(grayImage, grayImage, 50, 1);
//OpencvHelper.Imshow("旋转", grayImage); //建立图像的梯度幅值
Mat gradientImage = new Mat();
OpencvHelper.CvConvertScaleAbs(grayImage, gradientImage);
ShowImage("梯度幅值", gradientImage); //对图片进行相应的模糊化,使一些噪点消除
Mat blurImage = new Mat();
Mat thresholdImage = new Mat();
OpencvHelper.BlurImage(gradientImage, blurImage, thresholdImage);
ShowImage("二值化", blurImage); //二值化以后的图像,条形码之间的黑白没有连接起来,就要进行形态学运算,消除缝隙,相当于小型的黑洞,选择闭运算
//因为是长条之间的缝隙,所以需要选择宽度大于长度
Mat morphImage = new Mat();
OpencvHelper.MorphImage(thresholdImage, morphImage);
ShowImage("闭运算", morphImage); //现在要让条形码区域连接在一起,所以选择膨胀腐蚀,而且为了保持图形大小基本不变,应该使用相同次数的膨胀腐蚀
//先腐蚀,让其他区域的亮的地方变少最好是消除,然后膨胀回来,消除干扰,迭代次数根据实际情况选择
OpencvHelper.DilationErosionImage(morphImage);
ShowImage("膨胀腐蚀", morphImage); Mat[] contours = new Mat[];
List<double> OutArray = new List<double>();
//接下来对目标轮廓进行查找,目标是为了计算图像面积
Cv2.FindContours(morphImage, out contours, OutputArray.Create(OutArray), RetrievalModes.External, ContourApproximationModes.ApproxSimple);
//看看轮廓图像
//Cv2.DrawContours(srcImage, contours, -1, Scalar.Yellow);
//OpencvHelper.Imshow("目标轮廓", srcImage); //计算轮廓的面积并且存放
for (int i = ; i < OutArray.Count; i++)
{
OutArray[i] = contours[i].ContourArea(false);
} List<string> codes = new List<string>();
int num = ;
while (num < ) //找出10个面积最大的矩形
{
//找出面积最大的轮廓
double minValue, maxValue;
OpenCvSharp.Point minLoc, maxLoc;
Cv2.MinMaxLoc(InputArray.Create(OutArray), out minValue, out maxValue, out minLoc, out maxLoc);
//计算面积最大的轮廓的最小的外包矩形
RotatedRect minRect = Cv2.MinAreaRect(contours[maxLoc.Y]);
//找到了矩形的角度,但是这是一个旋转矩形,所以还要重新获得一个外包最小矩形
Rect myRect = Cv2.BoundingRect(contours[maxLoc.Y]);
//将扫描的图像裁剪下来,并保存为相应的结果,保留一些X方向的边界,所以对rect进行一定的扩张
myRect.X = myRect.X - (myRect.Width / );
myRect.Width = (int)(myRect.Width * 1.1); //TermCriteria termc = new TermCriteria(CriteriaType.MaxIter, 1, 1);
//Cv2.CamShift(srcImage, myRect, termc); //一次最大面积的
var a = contours.ToList();
a.Remove(contours[maxLoc.Y]);
contours = a.ToArray();
OutArray.Remove(OutArray[maxLoc.Y]); string code = DiscernBarCode(srcImage, myRect);
if(!string.IsNullOrEmpty(code))
{
//Cv2.Rectangle(srcImage, myRect, new Scalar(0, 255, 255), 3, LineTypes.AntiAlias);
codes.Add(code);
}
Cv2.Rectangle(srcImage, myRect, new Scalar(, , ), , LineTypes.AntiAlias);
num++;
if (contours.Count() <= )
break;
}
Image img2 = CreateImage(srcImage);
picFindContours.Image = img2;
txtcodess.Text = string.Join("\r\n", codes);
////找出面积最大的轮廓
//double minValue, maxValue;
//OpenCvSharp.Point minLoc, maxLoc;
//Cv2.MinMaxLoc(InputArray.Create(OutArray), out minValue, out maxValue, out minLoc, out maxLoc);
////计算面积最大的轮廓的最小的外包矩形
//RotatedRect minRect = Cv2.MinAreaRect(contours[maxLoc.Y]);
////为了防止找错,要检查这个矩形的偏斜角度不能超标
////如果超标,那就是没找到
//if (minRect.Angle < 2.0)
//{
// //找到了矩形的角度,但是这是一个旋转矩形,所以还要重新获得一个外包最小矩形
// Rect myRect = Cv2.BoundingRect(contours[maxLoc.Y]);
// //把这个矩形在源图像中画出来
// //Cv2.Rectangle(srcImage, myRect, new Scalar(0, 255, 255), 3, LineTypes.AntiAlias);
// //看看显示效果,找的对不对
// //Imshow("裁剪图片", srcImage);
// //将扫描的图像裁剪下来,并保存为相应的结果,保留一些X方向的边界,所以对rect进行一定的扩张
// myRect.X = myRect.X - (myRect.Width / 20);
// myRect.Width = (int)(myRect.Width * 1.1);
// Mat resultImage = new Mat(srcImage, myRect);
// //OpencvHelper.Imshow("结果图片", resultImage);
// Image img = CreateImage(resultImage);
// picCode.Image = img;
// DiscernBarcode(img);
// //看看轮廓图像
// Cv2.DrawContours(srcImage, contours, -1, Scalar.Red);
// //把这个矩形在源图像中画出来
// Cv2.Rectangle(srcImage, myRect, new Scalar(0, 255, 255), 3, LineTypes.AntiAlias);
// Image img2 = CreateImage(srcImage);
// picFindContours.Image = img2; // //string path = Path.GetDirectoryName(@g_sFilePath) + "\\Ok.png";
// //if (File.Exists(@path)) File.Delete(@path);//如果文件存在 则删除
// //if (!Cv2.ImWrite(@path, resultImage))
//}
srcImage.Dispose();
} private void HandelCode(Mat srcImage, Rect myRect, Mat[] contours)
{
Mat resultImage = new Mat(srcImage, myRect);
Image img = CreateImage(resultImage);
picCode.Image = img;
DiscernBarcode(img);
//看看轮廓图像
Cv2.DrawContours(srcImage, contours, -1, Scalar.Red);
//把这个矩形在源图像中画出来
Cv2.Rectangle(srcImage, myRect, new Scalar(0, 255, 255), 3, LineTypes.AntiAlias);
//Image img2 = CreateImage(srcImage);
//picFindContours.Image = img2;
} private Image CreateImage(Mat resultImage)
{
byte[] bytes = resultImage.ToBytes();
MemoryStream ms = new MemoryStream(bytes);
return Bitmap.FromStream(ms, true);
} private void ShowImage(string name, Mat resultImage)
{
//Image img = CreateImage(resultImage);
//frmShowImage frm = new frmShowImage(name, img);
//frm.ShowDialog();
} /// <summary>
/// 解析条形码图片
/// </summary>
private string DiscernBarCode(Mat srcImage, Rect myRect)
{
try
{
Mat resultImage = new Mat(srcImage, myRect);
Image img = CreateImage(resultImage);
Bitmap pImg = MakeGrayscale3((Bitmap)img);
BarcodeReader reader = new BarcodeReader();
reader.Options.CharacterSet = "UTF-8";
Result result = reader.Decode(new Bitmap(pImg));
Console.Write(result);
if (result != null)
return result.ToString();
else
return "";
}
catch (Exception ex)
{
Console.Write(ex);
return "";
}
} /// <summary>
/// 解析条形码图片
/// </summary>
private void DiscernBarcode(Image primaryImage)
{
//Bitmap pImg = MakeGrayscale3((Bitmap)primaryImage);
picHandel.Image = primaryImage;
BarcodeReader reader = new BarcodeReader();
reader.Options.CharacterSet = "UTF-8";
Result result = reader.Decode(new Bitmap(primaryImage));//Image.FromFile(path)
Console.Write(result);
if (result != null)
txtBarCode.Text = result.ToString();
else
txtBarCode.Text = ""; //watch.Start();
//watch.Stop();
//TimeSpan timeSpan = watch.Elapsed;
//MessageBox.Show("扫描执行时间:" + timeSpan.TotalMilliseconds.ToString()); //using (ZBar.ImageScanner scanner = new ZBar.ImageScanner())
//{
// scanner.SetConfiguration(ZBar.SymbolType.None, ZBar.Config.Enable, 0);
// scanner.SetConfiguration(ZBar.SymbolType.CODE39, ZBar.Config.Enable, 1);
// scanner.SetConfiguration(ZBar.SymbolType.CODE128, ZBar.Config.Enable, 1); // List<ZBar.Symbol> symbols = new List<ZBar.Symbol>();
// symbols = scanner.Scan((Image)pImg);
// if (symbols != null && symbols.Count > 0)
// {
// //string result = string.Empty;
// //symbols.ForEach(s => result += "条码内容:" + s.Data + " 条码质量:" + s.Type + Environment.NewLine);
// txtBarCode.Text = symbols.FirstOrDefault().Data;
// }
// else
// {
// txtBarCode.Text = "";
// }
//}
}
截图出来的条形码进行灰度处理
/// <summary>
/// 处理图片灰度
/// </summary>
/// <param name="original"></param>
/// <returns></returns>
public static Bitmap MakeGrayscale3(Bitmap original)
{
//create a blank bitmap the same size as original
Bitmap newBitmap = new Bitmap(original.Width, original.Height);
//get a graphics object from the new image
Graphics g = Graphics.FromImage(newBitmap);
//create the grayscale ColorMatrix
System.Drawing.Imaging.ColorMatrix colorMatrix = new System.Drawing.Imaging.ColorMatrix(
new float[][]
{
new float[] {.3f, .3f, .3f, , },
new float[] {.59f, .59f, .59f, , },
new float[] {.11f, .11f, .11f, , },
new float[] {, , , , },
new float[] {, , , , }
});
//create some image attributes
ImageAttributes attributes = new ImageAttributes();
//set the color matrix attribute
attributes.SetColorMatrix(colorMatrix);
//draw the original image on the new image
//using the grayscale color matrix
g.DrawImage(original, new Rectangle(, , original.Width, original.Height),
, , original.Width, original.Height, GraphicsUnit.Pixel, attributes);
//dispose the Graphics object
g.Dispose();
return newBitmap;
}
效果图:
c#OpenCVSharp+Zxing识别条形码的更多相关文章
- 实例源码--ZXing识别条形码和二维码识别源码
下载源码 技术要点: 1.ZXing库的 使用 2.识别条形码和二 维码 3.自定义视图 4.源码带有非常详 细的中文注释 ...... 详细介绍: 1.ZXing库 ZXing是个很经典的条码/ ...
- 在Android上使用ZXing识别条形码/二维码
越来越多的手机具备自动对焦的拍摄功能,这也意味着这些手机可以具备条码扫描的功能.......手机具备条码扫描的功能,可以优化购物流程,快速存储电子名片(二维码)等. 本文使用ZXing 1.6实现条码 ...
- Java 扫描识别条形码图片
1.条形码扫描识别的实现方法及步骤 本文以Java代码示例介绍如何来扫描和识别条形码图片.这里使用免费条码工具 Free Spire.Barcode for Java,调用BarcodeScanner ...
- 在C#中使用ZBar识别条形码
目录: 一.识别库 二.从一张图片中提取多个条形码 三.注意事项 从博客园学了很多,本着分享的目的,希望后来者遇到类似问题时,不必重复造轮子,早点下班回家^-^. 一.识别库 目前主流的识别库主要有Z ...
- ZXing生成条形码、二维码、带logo二维码
采用的是开源的ZXing,Maven配置如下,jar包下载地址,自己选择版本下载,顺便推荐下Maven Repository <!-- https://mvnrepository.com/art ...
- zxing 生成条形码
private Bitmap Out1DImg() { // 1.设置条形码规格 EncodingOptions encodeOption = new EncodingOptions(); encod ...
- Java 创建/识别条形码、二维码
条形码(Barcode)是将宽度不等的多个黑条和空白,按照一定的编码规则排列,用以表达一组信息的图形标识符.常用于标示物品的生产国.制造厂家.商品名称.生产日期.图书分类号.邮件起止地点.类别.日期等 ...
- C# vb .NET读取多个识别条形码线性条码
有时候,我们不确定数据源图片中到底有多少个条码,这些条码有可能是同一个类型的,也可能是不同类型的,如何在C#,vb等.NET平台语言里实现快速准确读取呢?答案是使用SharpBarcode! Shar ...
- C# vb .NET从pdf读取识别条形码线性条码
如何在C#,vb等.NET平台语言里实现快速准确从pdf文件读取,或者从Pdf指定页面读取条形码或QR二维码呢?答案是使用SharpBarcode! SharpBarcode是C#快速高效.准确的条形 ...
随机推荐
- ubuntu hadoop安装
参考: https://wangchangchung.github.io/2017/09/28/Ubuntu-16-04%E4%B8%8A%E5%AE%89%E8%A3%85Hadoop%E5%B9% ...
- 对官网vue事件修饰符串联的顺序如何理解?
官网有一个提醒:使用修饰符时,顺序很重要:相应的代码会以同样的顺序产生.因此,用 v-on:click.prevent.self 会阻止所有的点击,而 v-on:click.self.prevent ...
- OpenCL使用CL_MEM_USE_HOST_PTR存储器对象属性与存储器映射
随着OpenCL的普及,现在有越来越多的移动设备以及平板.超级本等都支持OpenCL异构计算.而这些设备与桌面计算机.服务器相比而言性能不是占主要因素的,反而能耗更受人关注.因此,这些移动设备上的GP ...
- smarty 模板几个例子(变量调节器)
一.assign和display方法的使用以及几个变量调节器 header("content-type:text/html;charset=utf-8");//加载Smarty引擎 ...
- osg help
#ifdef _WIN32#include <Windows.h>#endif // _WIN32 #include <osgViewer/Viewer>#include &l ...
- 阶段5 3.微服务项目【学成在线】_day03 CMS页面管理开发_02-自定义查询页面-服务端-接口开发
在Service中实现自定义查询 StringUtils.isNotEmpty()是这个包下的org.apache.commons.lang3.StringUtils; 再设置其他的条件 定义Exam ...
- jExcelAPI 操作 Excel 文件
在开源世界中,有两套比较有影响的API可 供使用,一个是POI,一个是jExcelAPI.其中功能相对POI比较弱一点.但jExcelAPI对中文支持非常好,API是纯Java的, 并不 依赖Wind ...
- delphi7 clientdataset 详解
delphi Midas SQLServer的自增字段的处理1.新增时,表中有自增字段,但是不希望用Refresh,直接ApplyUpdates直接看见自增字段的值在DataSetProvider.A ...
- UICollectionview的头视图和尾视图
UITableView有头视图和尾视图,那么UICollectionView有没有头视图和尾视图呢? 答案是有的. 1.新建一个类,必须继承自 UICollectionReusableView. 2. ...
- 如何屏蔽掉烦人的www.google-analytics.com
有时候在开发的网站项目中会加载谷歌分析的js,并且加载的非常慢导致浏览器一直在转圈圈. 按下面的方法可屏蔽掉烦人的www.google-analytics.com 现在想只有屏蔽掉google-a ...