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("\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, 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;
        } 

 

效果圖:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM