zoukankan      html  css  js  c++  java
  • 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() == 3)
                {
                    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: 1, yorder: 0, ksize: -1);
                Cv2.Sobel(grayImage, gradientYImage, MatType.CV_32F, xorder: 0, yorder: 1, ksize: -1);
                //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(6, 6));
                //Cv2.GaussianBlur(gradientImage, blurImage, new OpenCvSharp.Size(7, 7), 0);//Size必须是奇数
                //模糊化以后进行阈值化,得到到对应的黑白二值化图像,二值化的阈值可以根据实际情况调整
                Cv2.Threshold(blurImage, thresholdImage, 210, 255, ThresholdTypes.Binary);
                //看看二值化图像
                //Imshow("二值化图像", thresholdImage);
            }
            /// <summary>
            /// 闭运算
            /// </summary>
            public static void MorphImage(Mat thresholdImage, Mat morphImage)
            {
                //二值化以后的图像,条形码之间的黑白没有连接起来,就要进行形态学运算,消除缝隙,相当于小型的黑洞,选择闭运算
                //因为是长条之间的缝隙,所以需要选择宽度大于长度
                Mat kernel = Cv2.GetStructuringElement(MorphShapes.Rect, new OpenCvSharp.Size(21, 7));
                Cv2.MorphologyEx(thresholdImage, morphImage, MorphTypes.Close, kernel);
                //看看形态学操作以后的图像
                //Imshow("闭运算", morphImage);
            }
            /// <summary>
            /// 膨胀腐蚀
            /// </summary>
            public static void DilationErosionImage(Mat morphImage)
            {
                //现在要让条形码区域连接在一起,所以选择膨胀腐蚀,而且为了保持图形大小基本不变,应该使用相同次数的膨胀腐蚀
                //先腐蚀,让其他区域的亮的地方变少最好是消除,然后膨胀回来,消除干扰,迭代次数根据实际情况选择
                OpenCvSharp.Size size = new OpenCvSharp.Size(3, 3);
                OpenCvSharp.Point point = new OpenCvSharp.Point(-1, -1);
                Cv2.Erode(morphImage, morphImage, Cv2.GetStructuringElement(MorphShapes.Rect, size), point, 4);
                Cv2.Dilate(morphImage, morphImage, Cv2.GetStructuringElement(MorphShapes.Rect, size), point, 4);
                //看看形态学操作以后的图像
                //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(0);
                }
                //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[10000];
                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 = 0; i < OutArray.Count; i++)
                {
                    OutArray[i] = contours[i].ContourArea(false);
                }
    
                List<string> codes = new List<string>();
                int num = 0;
                while (num < 10) //找出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 / 20);
                    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(0, 255, 255), 3, LineTypes.AntiAlias);
                    num++;
                    if (contours.Count() <= 0)
                        break;
                }
                Image img2 = CreateImage(srcImage);
                picFindContours.Image = img2;
                txtcodess.Text = string.Join("
    ", 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, 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;
            } 

    效果图:

  • 相关阅读:
    Docker学习总结(四)--应用部署
    strcat的由来
    ubuntu man不到pthread_mutex_XX
    string::front
    string::find_last_of
    string::find_last_not_of
    string::find_first_of
    string::find_first_not_of
    string::erase
    string::empty
  • 原文地址:https://www.cnblogs.com/shuaimeng/p/10996401.html
Copyright © 2011-2022 走看看