先添加引用OpenCVSharp+zxing,上我写的代码
public class OpencvHelper { ////// 灰度图 /// /// 未处理的mat容器 /// 灰度图mat容器 public static void CvGrayImage(Mat srcImage, Mat grayImage) { if (srcImage.Channels() == 3) { Cv2.CvtColor(srcImage, grayImage, ColorConversionCodes.BGR2GRAY); } else { grayImage = srcImage.Clone(); } //Imshow("灰度图", grayImage); } ////// 图像的梯度幅值 /// /// public static void CvConvertScaleAbs(Mat grayImage, Mat gradientImage) { //建立图像的梯度幅值 Mat gradientXImage = new Mat(); Mat gradientYImage = new Mat(); Cv2.Scharr(grayImage, gradientXImage, MatType.CV_32F, 1, 0); Cv2.Scharr(grayImage, gradientYImage, MatType.CV_32F, 0, 1); //因为我们需要的条形码在需要X方向水平,所以更多的关注X方向的梯度幅值,而省略掉Y方向的梯度幅值 Cv2.Subtract(gradientXImage, gradientYImage, gradientImage); //归一化为八位图像 Cv2.ConvertScaleAbs(gradientImage, gradientImage); //看看得到的梯度图像是什么样子 //Imshow("图像的梯度幅值", gradientImage); } ////// 二值化图像 /// public static void BlurImage(Mat gradientImage, Mat blurImage, Mat thresholdImage) { //对图片进行相应的模糊化,使一些噪点消除 //OpenCvSharp.Size size1 = new OpenCvSharp.Size(12, 12);(9,9) OpenCvSharp.Size size1 = new OpenCvSharp.Size(6, 6); Cv2.Blur(gradientImage, blurImage, size1); //模糊化以后进行阈值化,得到到对应的黑白二值化图像,二值化的阈值可以根据实际情况调整 Cv2.Threshold(blurImage, thresholdImage, 210, 255, ThresholdTypes.Binary); //看看二值化图像 //Imshow("二值化图像", thresholdImage); } ////// 闭运算 /// public static void MorphImage(Mat thresholdImage, Mat morphImage) { //二值化以后的图像,条形码之间的黑白没有连接起来,就要进行形态学运算,消除缝隙,相当于小型的黑洞,选择闭运算 //因为是长条之间的缝隙,所以需要选择宽度大于长度 OpenCvSharp.Size size2 = new OpenCvSharp.Size(21, 7); Mat kernel = Cv2.GetStructuringElement(MorphShapes.Rect, size2); Cv2.MorphologyEx(thresholdImage, morphImage, MorphTypes.Close, kernel); //看看形态学操作以后的图像 //Imshow("闭运算", morphImage); } ////// 膨胀腐蚀 /// public static void DilationErosionImage(Mat morphImage) { //现在要让条形码区域连接在一起,所以选择膨胀腐蚀,而且为了保持图形大小基本不变,应该使用相同次数的膨胀腐蚀 //先腐蚀,让其他区域的亮的地方变少最好是消除,然后膨胀回来,消除干扰,迭代次数根据实际情况选择 OpenCvSharp.Size size3 = new OpenCvSharp.Size(3, 3); OpenCvSharp.Point point = new OpenCvSharp.Point(-1, -1); Cv2.Erode(morphImage, morphImage, Cv2.GetStructuringElement(MorphShapes.Rect, size3), point, 4); Cv2.Dilate(morphImage, morphImage, Cv2.GetStructuringElement(MorphShapes.Rect, size3), point, 4); //看看形态学操作以后的图像 //Imshow("膨胀腐蚀", morphImage); } ////// 显示处理后的图片 /// /// 处理过程名称 /// 图片盒子 public static void Imshow(string name, Mat srcImage) { Cv2.ImShow(name, srcImage); Cv2.WaitKey(0); } }
private void OpenCV() { //读取原图_imageFilePath:图片地址 Mat srcImage = new Mat(_imageFilePath, ImreadModes.Color); if (srcImage.Empty()) { return; } //图像转换为灰度图像 Mat grayImage = new Mat(); OpencvHelper.CvGrayImage(srcImage, grayImage); //建立图像的梯度幅值 Mat gradientImage = new Mat(); OpencvHelper.CvConvertScaleAbs(grayImage, gradientImage); //对图片进行相应的模糊化,使一些噪点消除 Mat blurImage = new Mat(); Mat thresholdImage = new Mat(); OpencvHelper.BlurImage(gradientImage, blurImage, thresholdImage); //二值化以后的图像,条形码之间的黑白没有连接起来,就要进行形态学运算,消除缝隙,相当于小型的黑洞,选择闭运算 //因为是长条之间的缝隙,所以需要选择宽度大于长度 Mat morphImage = new Mat(); OpencvHelper.MorphImage(thresholdImage, morphImage); //现在要让条形码区域连接在一起,所以选择膨胀腐蚀,而且为了保持图形大小基本不变,应该使用相同次数的膨胀腐蚀 //先腐蚀,让其他区域的亮的地方变少最好是消除,然后膨胀回来,消除干扰,迭代次数根据实际情况选择 OpencvHelper.DilationErosionImage(morphImage); Mat[] contours = new Mat[10000]; ListOutArray = new List (); //接下来对目标轮廓进行查找,目标是为了计算图像面积 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 = 0; i < OutArray.Count; i++) { OutArray[i] = contours[i].ContourArea(); } //找出面积最大的轮廓 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)) } } private Image CreateImage(Mat resultImage) { byte[] bytes = resultImage.ToBytes(); MemoryStream ms = new MemoryStream(bytes); return Bitmap.FromStream(ms, true); } /// /// 解析条形码图片 /// private void DiscernBarcode(Image image) { BarcodeReader reader = new BarcodeReader(); reader.Options.CharacterSet = "UTF-8"; Result result = reader.Decode(new Bitmap(image));//Image.FromFile(path) Console.Write(result); if (result != null) txtBarCode.Text = result.ToString(); }
截图出来的条形码进行灰度处理
////// 处理图片灰度 /// /// ///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, 0, 0}, new float[] {.59f, .59f, .59f, 0, 0}, new float[] {.11f, .11f, .11f, 0, 0}, new float[] {0, 0, 0, 1, 0}, new float[] {0, 0, 0, 0, 1} }); //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(0, 0, original.Width, original.Height), 0, 0, original.Width, original.Height, GraphicsUnit.Pixel, attributes); //dispose the Graphics object g.Dispose(); return newBitmap; }
效果图: