using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Runtime.InteropServices; using AR; #if EMGU using Emgu.CV; using Emgu.CV.Structure; #endif #if V22 using Euresys.Open_eVision_22_12; #else using Euresys.Open_eVision_2_11; #endif namespace Project { public static class Util_Vision { public struct SCodeData { public Point[] corner; public string data; public string model; public string version; public string level; public string sid; public void SetSID(string sid_) { sid = sid_; } public Rectangle rect { get { if (corner == null || corner.Length != 4) return Rectangle.Empty; var minx = corner.Min(t => t.X); var maxx = corner.Max(t => t.X); var miny = corner.Min(t => t.Y); var maxy = corner.Max(t => t.Y); return new Rectangle((int)minx, (int)miny, (int)(maxx - minx), (int)(maxy - miny)); } } public float intersectPerc(Rectangle r) { //입력한 영역이 얼마나 겹쳐있는지 if (rect.IsEmpty) return 0f; //현재 영역이랑 겹치는 영역이 없다면 0f if (rect.IntersectsWith(r) == false) return 0f; var cx = r.Left + r.Width / 2f; var cy = r.Top + r.Height / 2f; if (rect.Contains((int)cx, (int)cy)) return 1f; if (rect.Contains(r)) return 1f; return 0f; } } public static void DisplayQRData(List DataArr, Rectangle roi, arCtl.ImageBoxEvision iv) { iv.Shapes.Clear(); foreach (var item in DataArr) { var p = new PointF[4]; p[0] = new PointF(item.corner[0].X + roi.Left, item.corner[0].Y + roi.Top); p[1] = new PointF(item.corner[1].X + roi.Left, item.corner[1].Y + roi.Top); p[2] = new PointF(item.corner[2].X + roi.Left, item.corner[2].Y + roi.Top); p[3] = new PointF(item.corner[3].X + roi.Left, item.corner[3].Y + roi.Top); iv.Shapes.AddLine(p[0], p[1], Color.Gold, 5); iv.Shapes.AddLine(p[1], p[2], Color.Gold, 5); iv.Shapes.AddLine(p[2], p[3], Color.Gold, 5); iv.Shapes.AddLine(p[3], p[0], Color.Gold, 5); if (item.data.isEmpty() == false) { iv.Shapes.AddText(item.corner[1], item.data, Color.Black, 200f); } } } private static List GetGainOffsetList(string gainstr) { var retval = new List(); var list = gainstr.Split(';'); foreach (var item in list) { var ptitem = item.Split(','); if (ptitem.Length == 2) { retval.Add(new System.Drawing.PointF(float.Parse(ptitem[0]), float.Parse(ptitem[1]))); } } return retval; } // static SoftekBarcodeNet.BarcodeReader barcode = null;// new SoftekBarcodeNet.BarcodeReader(); static List ReadQR_EVision(EImageBW8 TargetImage, EQRCodeReader EQRCode1, RectangleF rect) { var retval = new System.Collections.Generic.List(); EQRCode1.SearchField = TargetImage; var EQRCode2Result = EQRCode1.Read(); for (int j = 0; j < EQRCode2Result.Length; j++) { var QrData = EQRCode2Result[j]; if (QrData.IsDecodingReliable) { var geo = QrData.Geometry; var pos = geo.Position; var cornous = pos.Corners; var resultData = new Util_Vision.SCodeData(); //테두리좌표 읽기 var resultcorns = new List(); for (int k = 0; k < cornous.Length; k++) { var con = cornous[k]; resultcorns.Add(new Point((int)(con.X + rect.X), (int)(con.Y + rect.Y))); con.Dispose(); } resultData.corner = resultcorns.ToArray(); //데이터읽기 resultData.model = QrData.Model.ToString(); resultData.version = QrData.Version.ToString(); resultData.level = QrData.Level.ToString(); resultData.sid = string.Empty; //바코드데이터확인 var decodestr = QrData.DecodedStream; resultData.data = string.Empty; foreach (var item in decodestr.DecodedStreamParts) { resultData.data += System.Text.Encoding.Default.GetString(item.DecodedData); item.Dispose(); } //var c = new Class.CAmkorSTDBarcode(resultData.data); //if (c.isValid) resultData.sid = c.SID; //else resultData.sid = string.Empty; //if (c.isValid) break; //else resultData.data = string.Empty; //결과데이터 추가 if (resultData.data.isEmpty() == false) { if (retval.Where(t => t.data == resultData.data).Any() == false) { retval.Add(resultData); } } decodestr.Dispose(); decodestr = null; cornous = null; pos.Dispose(); geo.Dispose(); } QrData.Dispose(); } return retval; } //static List ReadQR_Softtek(EImageBW8 Image, RectangleF roi, Boolean useMedianfilter = false, int timeout = 50) //{ // if (barcode == null) // { // barcode = new SoftekBarcodeNet.BarcodeReader(); // barcode.LicenseKey = "PC6q6K38G6bG9EWMlQxDr4O8aQzJWPSB"; // Pub.log.Add("softtek barcode init"); // // Turn on the barcode types you want to read. // // Turn off the barcode types you don't want to read (this will increase the speed of your application) // barcode.ReadCode128 = false; // barcode.ReadCode39 = false; // barcode.ReadCode25 = false; // barcode.ReadEAN13 = false; // barcode.ReadEAN8 = false; // barcode.ReadUPCA = false; // barcode.ReadUPCE = false; // barcode.ReadCodabar = false; // barcode.ReadPDF417 = false; // barcode.ReadDataMatrix = true; // barcode.ReadDatabar = false; // barcode.ReadMicroPDF417 = false; // barcode.ReadQRCode = true; // // If you want to read more than one barcode then set Multiple Read to 1 // // Setting MutlipleRead to False will make the recognition faster // barcode.MultipleRead = false; // barcode.MaxBarcodesPerPage = 1; // barcode.UseFastScan = true; // // You may need to set a small quiet zone if your barcodes are close to text and pictures in the image. // // A value of zero uses the default. // barcode.QuietZoneSize = 0; //사용하면 느려짐 // // LineJump controls the frequency at which scan lines in an image are sampled. // // The default is 9 - decrease this for difficult barcodes. // barcode.LineJump = 3; //속도가 빨라지나 결과 검출 영향 많이 줌 // // The value is a mask where 1 = Left to Right, 2 = Top to Bottom, 4 = Right To Left, 8 = Bottom to Top // barcode.ScanDirection = 15; //방향을 조정해주면 속도 가 빨라짐 // barcode.ColorProcessingLevel = 2; // barcode.MinLength = 4; // barcode.MaxLength = 9999; // } // barcode.MedianFilter = useMedianfilter;//Pub.setting.CameraMeanFilter; // barcode.TimeOut = timeout;// 50;// Pub.setting.CameraTimeOut; // int nBarCode; // var retval = new List(); // var Scan0 = Image.GetImagePtr(); // using (var bmp = new Bitmap(Image.Width, Image.Height, Image.RowPitch, System.Drawing.Imaging.PixelFormat.Format8bppIndexed, Scan0)) // { // //비트맵 개체로 데이터를 읽어본다 // nBarCode = barcode.ScanBarCodeFromBitmap(bmp); // if (nBarCode <= -6) // { // //Result.Text += "License key error: either an evaluation key has expired or the license key is not valid for processing pdf documents\r\n"; // } // else if (nBarCode < 0) // { // //Result.Text += "ScanBarCode returned error number "; // //Result.Text += nBarCode.ToString(); // //Result.Text += "\r\n"; // //Result.Text += "Last Softek Error Number = "; // //Result.Text += barcode.GetLastError().ToString(); // //Result.Text += "\r\n"; // //Result.Text += "Last Windows Error Number = "; // //Result.Text += barcode.GetLastWinError().ToString(); // //Result.Text += "\r\n"; // } // else if (nBarCode == 0) // { // //Result.Text += "No barcode found on this image\r\n"; // } // else // { // //Result.Text += "\r\n"; // for (int i = 1; i <= nBarCode; i++) // { // var rect = barcode.GetBarStringRect(i); // var resultData = new SCodeData() // { // corner = new Point[] { // new Point((int)(rect.Left+roi.X),(int)(rect.Top + roi.Y)), // new Point((int)(rect.Right+roi.X),(int)(rect.Top+roi.Y)), // new Point((int)(rect.Left+roi.X),(int)(rect.Bottom+roi.Y)), // new Point((int)(rect.Right+roi.X),(int)(rect.Bottom+roi.Y)) // }, // data = barcode.GetBarString(i), // level = string.Empty, // model = string.Empty, // sid = string.Empty, // version = string.Empty, // }; // //결과데이터 추가 // if (resultData.data.isEmpty() == false) // { // if (retval.Where(t => t.data == resultData.data).Any() == false) // { // retval.Add(resultData); // } // } // } // } // return retval; // } //} public static Tuple, List, List, double> DetectQR( EImageBW8 EBW8Image4, arCtl.ImageBoxEvision iv1, int idx, out string Message, string erodevaluestr = "3,1", string gainoffsetlist = "2,1", uint blob_area_min = 20000, uint blob_area_max = 70000, float blob_sigmaxy = 500f, float blob_sigmayy = 500f, float blob_sigmaxx = 5000f, float blob_minw = 50, float blob_maxw = 350, float blob_minh = 50, float blob_maxh = 350, //bool addSofttekRead = false, string maskfile = "", string finddata = ""//, // Image DispImage = null ) { Message = string.Empty; var pts = new System.Collections.Generic.List(); System.Diagnostics.Stopwatch wat = new System.Diagnostics.Stopwatch(); wat.Restart(); List ros = new List(); List whiterects = new List(); uint objectCountT = 0; //Blob 으로 바코드영역을 찾는다 using (ECodedImage2 codedImage4 = new ECodedImage2()) // ECodedImage2 instance { using (EImageEncoder codedImage4Encoder = new EImageEncoder()) // EIma { using (EObjectSelection codedImage4ObjectSelection = new EObjectSelection()) { codedImage4ObjectSelection.FeretAngle = 0.00f; //기본이미지에서 흰색영역을 찾아서 해당 영역만 처리한다. using (var psegment = codedImage4Encoder.GrayscaleSingleThresholdSegmenter) //.BlackLayerEncoded = true; { psegment.BlackLayerEncoded = false; psegment.WhiteLayerEncoded = true; psegment.Mode = EGrayscaleSingleThreshold.MaxEntropy; } //마스크적용 - 210209 if (maskfile.isEmpty() == false && System.IO.File.Exists(maskfile)) { using (var maskmig = new EImageBW8()) { maskmig.Load(maskfile);//이미지불러오지 EasyImage.Oper(EArithmeticLogicOperation.BitwiseAnd, EBW8Image4, maskmig, EBW8Image4); } } //흰색데이터강조 using (var deilimage = new EImageBW8(EBW8Image4.Width, EBW8Image4.Height)) { EasyImage.DilateBox(EBW8Image4, deilimage, 30); codedImage4Encoder.Encode(deilimage, codedImage4); codedImage4ObjectSelection.Clear(); codedImage4ObjectSelection.AddObjects(codedImage4); codedImage4ObjectSelection.RemoveUsingUnsignedIntegerFeature(EFeature.Area, 50000, ESingleThresholdMode.LessEqual); for (uint i = 0; i < codedImage4ObjectSelection.ElementCount; i++) { var elm = codedImage4ObjectSelection.GetElement(i); var x = elm.BoundingBoxCenterX - elm.BoundingBoxWidth / 2f; var y = elm.BoundingBoxCenterY - elm.BoundingBoxHeight / 2f; var w = elm.BoundingBoxWidth; var h = elm.BoundingBoxHeight; whiterects.Add(new Rectangle((int)x, (int)y, (int)w, (int)h)); elm.Dispose(); } } //침식해서 바코드영역이 뭉치도록 한다. (바코드는 검은색 영역이 된다) List erodevalues = new List(); var erodebuffer = erodevaluestr.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); foreach (var erbuf in erodebuffer) erodevalues.Add(uint.Parse(erbuf)); using (var psegment = codedImage4Encoder.GrayscaleSingleThresholdSegmenter) //.BlackLayerEncoded = true; { psegment.BlackLayerEncoded = true; psegment.WhiteLayerEncoded = false; } //QR코드예상위치를 저장할 영역변수 //찾은 흰색영역에서 작업을 진행한다 foreach (var roi in whiterects) { //각영역을 erod 처리하여 추가 영역을 찾는다 using (EROIBW8 roimage = new EROIBW8()) { roimage.Attach(EBW8Image4); roimage.SetPlacement(roi.X, roi.Y, roi.Width, roi.Height); for (int maxtype = 0; maxtype < 2; maxtype++) { //침식데이터도 여러개를 사용한다. foreach (var erodevalue in erodevalues) { //Erode image using (var ErodeImageBW8 = new EImageBW8(roimage.Width, roimage.Height)) { EasyImage.ErodeBox(roimage, ErodeImageBW8, erodevalue); using (var psegment = codedImage4Encoder.GrayscaleSingleThresholdSegmenter) { if (maxtype == 0) psegment.Mode = EGrayscaleSingleThreshold.MinResidue; else if (maxtype == 1) psegment.Mode = EGrayscaleSingleThreshold.MaxEntropy; else if (maxtype == 2) psegment.Mode = EGrayscaleSingleThreshold.IsoData; } codedImage4Encoder.Encode(ErodeImageBW8, codedImage4); codedImage4ObjectSelection.Clear(); codedImage4ObjectSelection.AddObjects(codedImage4); codedImage4ObjectSelection.AttachedImage = ErodeImageBW8; //너무큰 개체를 제거한다. codedImage4ObjectSelection.RemoveUsingUnsignedIntegerFeature(EFeature.Area, blob_area_min, ESingleThresholdMode.LessEqual); codedImage4ObjectSelection.RemoveUsingUnsignedIntegerFeature(EFeature.Area, blob_area_max, ESingleThresholdMode.GreaterEqual); codedImage4ObjectSelection.RemoveUsingFloatFeature(EFeature.BoundingBoxWidth, blob_minw, ESingleThresholdMode.LessEqual); codedImage4ObjectSelection.RemoveUsingFloatFeature(EFeature.BoundingBoxWidth, blob_maxw, ESingleThresholdMode.GreaterEqual); codedImage4ObjectSelection.RemoveUsingFloatFeature(EFeature.BoundingBoxHeight, blob_minh, ESingleThresholdMode.LessEqual); codedImage4ObjectSelection.RemoveUsingFloatFeature(EFeature.BoundingBoxHeight, blob_maxh, ESingleThresholdMode.GreaterEqual); //개체제거 codedImage4ObjectSelection.RemoveUsingFloatFeature(EFeature.SigmaXY, blob_sigmaxy, ESingleThresholdMode.GreaterEqual); //codedImage4ObjectSelection.RemoveUsingFloatFeature(EFeature.SigmaYY, blob_sigmayy, ESingleThresholdMode.LessEqual); codedImage4ObjectSelection.RemoveUsingFloatFeature(EFeature.SigmaXX, blob_sigmaxx, ESingleThresholdMode.GreaterEqual); //찾은데이터수량 var objectCount = codedImage4ObjectSelection.ElementCount; objectCountT += objectCount; //데이터표시 //찾은영역을 표시한다. for (uint i = 0; i < objectCount; i++) { //각 요소를 가져와서 처리한다. using (var elm = codedImage4ObjectSelection.GetElement(i)) { var x = elm.BoundingBoxCenterX - elm.BoundingBoxWidth / 2f; var y = elm.BoundingBoxCenterY - elm.BoundingBoxHeight / 2f; var w = elm.BoundingBoxWidth; var h = elm.BoundingBoxHeight; //절대좌표로 변경( x += roimage.TotalOrgX; y += roimage.TotalOrgY; var rect = new RectangleF(x, y, w, h); //이전 ROS에 겹치는 영역이 있다면 해당 ROS값을 확장해준다 bool find = false; for (int j = 0; j < ros.Count; j++) { if (ros[j].IntersectsWith(rect)) { //겹쳐있으니 영역을 확장해준다. var x1 = Math.Min(ros[j].X, rect.X); var y1 = Math.Min(ros[j].Y, rect.Y); var w1 = Math.Max(ros[j].Right, rect.Right) - x1; var h1 = Math.Max(ros[j].Bottom, rect.Bottom) - y1; ros[j] = new RectangleF(x1, y1, w1, h1); find = true; break; } } if (find == false) ros.Add(rect); //겹치는 영역이 없으니 추가한다. } } } } } } } //whitelist 에서 QR을 검색한다.(밝기조정없음) var find2 = false; //ROS에서 QR을 검색한다(밝기조정함) if (find2 == false) { foreach (var roi in ros) { //대상자료를 찾았다면 멈춘다 if (ReadQRData(EBW8Image4, roi.toRect(), gainoffsetlist, ref pts, finddata)) { find2 = true; break; } } } if (find2 == false) { foreach (var roi in whiterects) { //대상자료를 찾았다면 멈춘다 if (ReadQRData(EBW8Image4, roi, "1,0", ref pts, finddata)) { find2 = true; //대상을 찾았다면 더 진행하지 않는다 break; } } } } } } wat.Stop(); //검사시간 저장 var rosrect = ros.Select(t => new Rectangle((int)t.X, (int)t.Y, (int)t.Width, (int)t.Height)).ToList(); return new Tuple, List, List, double>(pts, whiterects, rosrect, wat.ElapsedMilliseconds); } /// /// 지정한 영역에서 QR코드를 검색합니다. /// /// /// 검색영역 /// 밝기변경값 /// 결과저장소 /// 특정검색데이터 /// static bool ReadQRData(EImageBW8 EBW8Image4, Rectangle rect, string gainoffsetlist, ref List pts, string finddata = "") { var retval = false; var GainOffsetList = GetGainOffsetList(gainoffsetlist); //해당영역을 ROI로 잡고 이미지를 검색한다 var RoiImage2 = new EROIBW8(); RoiImage2.Attach(EBW8Image4); //ROI로 사용할 영역의 오류를 체크해야한다 var rLeft = (int)rect.Left; var rTop = (int)rect.Top; if (rLeft < 0) rLeft = 0; if (rTop < 0) rTop = 0; var rWid = (int)rect.Width; var rHei = (int)rect.Height; //roi 오류수정 210621 if (rLeft + rWid > EBW8Image4.Width) rWid = EBW8Image4.Width - rLeft - 1; if (rTop + rHei > EBW8Image4.Height) rHei = EBW8Image4.Height - rTop - 1; var rourect = new Rectangle(rLeft, rTop, rWid, rHei); RoiImage2.SetPlacement(rourect.Left, rourect.Top, rourect.Width, rourect.Height); //ROI적용 var TargetImage = new EImageBW8(RoiImage2.Width, RoiImage2.Height); //밝기를 변경해서 처리해야한다 //int processCount = 9; foreach (var param in GainOffsetList) //원본, gain +-, offset +- 5가지 색상처리함 { //크기를 동일하게 맞춰야 한다 EasyImage.ScaleRotate(RoiImage2, RoiImage2.Width / 2f, RoiImage2.Height / 2f, TargetImage.Width / 2f, TargetImage.Height / 2f, 1.0f, 1.0f, 0f, TargetImage); //EasyImage.Copy(RoiImage, TargetImage); //밝기를 변경해서 사용할 것이므로 해당 변경된 밝기를 저장할 이미지를 생성한다. 210128 //var roiimg = new EImageBW8(TargetImage); //var param = Util_Vision.GetGainOffset(bright); if (param.X != 1.0f || param.Y != 0.0f) EasyImage.GainOffset(TargetImage, TargetImage, param.X, param.Y); //else RoiImage.CopyTo(TargetImage); ;// TargetImage.CopyTo(roiimg); //변경하지 않고 그대로 사용한다 using (EQRCodeReader EQRCode1 = new EQRCodeReader()) { //EQRCode1.DetectionMethod = 15; //모든방법으로 데이터를 찾는다 EQRCode1.TimeOut = 1000 * 1000; //timeout (1초) if (TargetImage.Width < 500 || TargetImage.Height < 500) EQRCode1.ForegroundDetectionThreshold = 3; var datas = ReadQR_EVision(TargetImage, EQRCode1, rect); if (datas.Any()) { //중복데이터는 제거한다 211211 foreach (var dataitem in datas) { if (pts.Where(t => t.data == dataitem.data).Any() == false) pts.Add(dataitem); else if (pts.Where(t => t.intersectPerc(dataitem.rect) > 0f).Any() == false) pts.Add(dataitem); //해당 데이터가 찾는 데이터라면 진행을 멈춘다 if (finddata.isEmpty() == false && dataitem.data.Equals(finddata)) { //검색대상 자료이므로 진행을 멈춘다 retval = true; break; } //해당데이터가 amkor standard 라면 문제없이 진행한다. var splitbuf = dataitem.data.Split(';'); if (splitbuf.Length > 6 && splitbuf[0].StartsWith("10")) { //amkor std 라벨이므로 진행하지 않습니다. retval = true; break; } } //pts.AddRange(datas); } //sDisplayQRInfo(datas, iv1); } if (retval == true) break; //대상자료르 찾았다면 진행을 멈춘다 } RoiImage2.Dispose(); TargetImage.Dispose(); TargetImage = null; return retval; } #if EMGU static void DisplayQRInfo(List datas, arImageViewer.EVision.UI.CustomControl1 iv1) #else static void DisplayQRInfo(List datas, arCtl.ImageBoxEvision iv1) #endif { if (iv1 == null) return; var idx = 0; foreach (var resultData in datas) { //var c = new Class.CAmkorSTDBarcode(resultData.data); //if (c.isValid) resultData.SetSID(c.SID); // = c.SID; //else resultData.SetSID(string.Empty); //결과데이터 추가 if (resultData.data.isEmpty() == false) { //자료가잇다면 표시한다. var dispvalue = $"#{idx}";// resultData.data; //if (c.isValid == false) dispvalue += "\n" + c.Message; //else if (c.DateError) dispvalue += "\n** Date Error **"; iv1.Shapes.AddText(resultData.corner[1].X + 1, resultData.corner[1].Y + 1, dispvalue, Color.Black, 50); iv1.Shapes.AddText(resultData.corner[1].X, resultData.corner[1].Y, dispvalue, Color.Yellow, 50); //if (DispImage != null) // CvInvoke.PutText(DispImage, // dispvalue, // new Point(resultData.corner[1].X, resultData.corner[1].Y), FontFace.HersheyDuplex, 2, // new Bgr(Color.Tomato).MCvScalar, 2); //찾은테두리를 화면에 표시한다. foreach (var pt in resultData.corner) { iv1.Shapes.AddPoint(pt, Color.Lime, 20f); //if (DispImage != null) //{ // CvInvoke.Line(DispImage, // new Point(pt.X - 10, pt.Y - 10), // new Point(pt.X + 10, pt.Y + 10), // new Bgr(Color.Lime).MCvScalar, 2); // CvInvoke.Line(DispImage, // new Point(pt.X + 10, pt.Y - 10), // new Point(pt.X - 10, pt.Y + 10), // new Bgr(Color.Lime).MCvScalar, 2); //} } } idx++; } } } }