0、背景
公司物流和财务部门想要把纸质单据做电子归档,需要识别单据上的二维码,拍照后保存到归档系统。
1、软件模块
1、WPF客户端
对接高拍仪驱动,控制拍照
读取图片使用opencv 处理图片,寻找二维码块
使用二维码读取api 读取二维码内容
上传图片和二维码数据到服务器
2、webapi 后端
图片上传接口
数据查询保存接口
单据查询页面
2、open cv 代码C#-Emgu
private void ShowBgrImage(System.Windows.Controls.Image oImage, Image<Bgr, byte> src)
{
this.Dispatcher.Invoke(() => {
oImage.Source = BitmapSourceConvert.ToBitmapSource(src);
});
}
private void ShowGrayImage(System.Windows.Controls.Image oImage, Image<Gray, byte> src)
{
this.Dispatcher.Invoke(() => {
oImage.Source = BitmapSourceConvert.ToBitmapSource(src);
});
}
public byte[] getImgByte(System.Drawing.Image image)
{
MemoryStream ms = new MemoryStream();
try
{
image.Save(ms, ImageFormat.Bmp);
byte[] bt = ms.GetBuffer();
return bt;
}
catch (Exception ex)
{
throw ex;
}
finally
{
ms.Close();
}
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
string sFile = AppDomain.CurrentDomain.BaseDirectory + "Test15.jpg";//"Test5.png";
System.Drawing.Image img = System.Drawing.Image.FromFile(sFile);
Bitmap barcodeBitmap = new Bitmap(img);
Image<Bgr, byte> img_src = new Image<Bgr, byte>(barcodeBitmap);
//1
this.ShowBgrImage(this.Img1, img_src);
//return;
//灰度化2
Image<Gray, byte> imput_gray = new Image<Gray, byte>(img_src.Size);
CvInvoke.CvtColor(img_src, imput_gray, ColorConversion.Bgr2Gray);
this.ShowGrayImage(this.Img2, imput_gray);
//计算x,y方向梯度,相加 3
Image<Gray, byte> grad_x1 = new Image<Gray, byte>(img_src.Size);
Image<Gray, byte> grad_y1 = new Image<Gray, byte>(img_src.Size);
Image<Gray, byte> grad_all = new Image<Gray, byte>(img_src.Size);
CvInvoke.Sobel(imput_gray, grad_x1, DepthType.Default, 0, 1, 3);
CvInvoke.Sobel(imput_gray, grad_y1, DepthType.Default, 1, 0, 3);
CvInvoke.Add(grad_x1, grad_y1, grad_all, null, DepthType.Default);
this.ShowGrayImage(this.Img3, grad_all);
// 高斯模糊 4
grad_all = grad_all.SmoothGaussian(11);
this.ShowGrayImage(this.Img4, grad_all);
// 二值化 5
CvInvoke.Threshold(grad_all, grad_all, 50, 255, ThresholdType.Binary);
this.ShowGrayImage(this.Img5, grad_all);
//消除裂缝 6
Mat oMat1 = CvInvoke.GetStructuringElement(Emgu.CV.CvEnum.ElementShape.Rectangle,
new System.Drawing.Size(8, 8), new System.Drawing.Point(0, 0));
CvInvoke.MorphologyEx(grad_all, grad_all, Emgu.CV.CvEnum.MorphOp.Close, oMat1,
new System.Drawing.Point(0, 0), 1, BorderType.Default,
new MCvScalar(255, 0, 0, 255));
this.ShowGrayImage(this.Img6, grad_all);
//膨胀与腐蚀(消除杂点)
Mat oMat2 = CvInvoke.GetStructuringElement(Emgu.CV.CvEnum.ElementShape.Rectangle,
new System.Drawing.Size(20, 20), new System.Drawing.Point(0, 0));
CvInvoke.Erode(grad_all, grad_all, oMat2, new System.Drawing.Point(0, 0), 4,
BorderType.Default, new MCvScalar(255, 0, 0, 255));
CvInvoke.Dilate(grad_all, grad_all, oMat2, new System.Drawing.Point(0, 0), 4,
BorderType.Default, new MCvScalar(255, 0, 0, 255));
this.ShowGrayImage(this.Img7, grad_all);
//查找轮廓,绘制轮廓
#region Find triangles and rectangles
List<Triangle2DF> triangleList = new List<Triangle2DF>();
List<RotatedRect> boxList = new List<RotatedRect>(); //a box is a rotated rectangle
using (VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint())
{
CvInvoke.FindContours(grad_all, contours, null, RetrType.List, ChainApproxMethod.ChainApproxSimple);
int count = contours.Size;
for (int i = 0; i < count; i++)
{
using (VectorOfPoint contour = contours[i])
using (VectorOfPoint approxContour = new VectorOfPoint())
{
CvInvoke.ApproxPolyDP(contour, approxContour, CvInvoke.ArcLength(contour, true) * 0.05, true);
if (CvInvoke.ContourArea(approxContour, false) > 500)
{
if (approxContour.Size == 3)
{
System.Drawing.Point[] pts = approxContour.ToArray();
triangleList.Add(new Triangle2DF(
pts[0],
pts[1],
pts[2]
));
}
else if (approxContour.Size == 4)
{
#region determine if all the angles in the contour are within [80, 100] degree
bool isRectangle = true;
System.Drawing.Point[] pts = approxContour.ToArray();
LineSegment2D[] edges = Emgu.CV.PointCollection.PolyLine(pts, true);
for (int j = 0; j < edges.Length; j++)
{
double angle = Math.Abs(
edges[(j + 1) % edges.Length].GetExteriorAngleDegree(edges[j]));
if (angle < 80 || angle > 100)
{
isRectangle = false;
break;
}
}
#endregion
if (isRectangle)
{
boxList.Add(CvInvoke.MinAreaRect(approxContour));
}
}
}
}
}
}
this.Img8.Source = BitmapSourceConvert.ToBitmap(barcodeBitmap);
//this.ShowGrayImage(this.Img8, grad_all);
foreach (RotatedRect box in boxList)
{
if(box.Size.Width < 50 || box.Size.Height < 50)
{//过滤小于50像素的块
continue;
}
if(box.Size.Width / box.Size.Height > 1.6 || box.Size.Height / box.Size.Width > 1.6)
{//过滤长宽比例大于2的块
continue;
}
System.Drawing.Point[] pts = Array.ConvertAll(box.GetVertices(), System.Drawing.Point.Round);
int x = 0, y = 0, length = 0, width = 0;
List<int> xList = new List<int>();
List<int> yList = new List<int>();
for (int i = 0; i < pts.Length; i++)
{
System.Drawing.Point point = pts[i];
System.Drawing.Point point1 = new System.Drawing.Point();
if (i == pts.Length - 1)
point1 = pts[0];
else
point1 = pts[i + 1];
Line oLine = new Line();
oLine.Stroke = new SolidColorBrush(Colors.Red);
oLine.StrokeThickness = 2;
oLine.X1 = point.X;
oLine.Y1 = point.Y;
oLine.X2 = point1.X;
oLine.Y2 = point1.Y;
this.CvMainZm.Children.Add(oLine);
xList.Add(point.X);
xList.Add(point1.X);
yList.Add(point.Y);
yList.Add(point1.Y);
}
x = xList.Min();
y = yList.Min();
length= xList.Max() - x;
width = yList.Max() - y;
TextBlock text = ReadQRCode(barcodeBitmap, x, y, length + 50, width + 50);
if(text != null)
{
//this.CvMainZm.Children.Add(text);
Console.WriteLine(text.Text);
}
}
#endregion
}
TextBlock ReadQRCode(Bitmap srcImg, int x, int y, int length, int width)
{
if(length <= 1 || width <= 1)
{
return null;
}
Bitmap clipedImg = new Bitmap(length, width);
System.Drawing.Rectangle srcRect = new System.Drawing.Rectangle(x, y, length, width);
Graphics g = Graphics.FromImage(clipedImg);
g.DrawImage(srcImg, 0, 0, srcRect, GraphicsUnit.Pixel);
g.Dispose();
////分割结束
////读取分割后的二维码
Image<Bgr, byte> img_src2 = new Image<Bgr, byte>(clipedImg);
Image<Gray, byte> imput_gray2 = new Image<Gray, byte>(clipedImg.Size);
CvInvoke.CvtColor(img_src2, imput_gray2, ColorConversion.Bgr2Gray);
//截取的二维码需要高斯模糊处理一下值是奇数
//如果是清晰的不要执行下面的2行
imput_gray2 = Sharpen(imput_gray2, clipedImg.Width, clipedImg.Height, 3, 3, 1);
imput_gray2 = imput_gray2.SmoothGaussian(5);
this.ShowGrayImage(this.Img1, imput_gray2);
Bitmap barcodeBitmap = new Bitmap(imput_gray2.ToBitmap());
ZXing.BarcodeReader re = new ZXing.BarcodeReader();
ZXing.Result result2 = re.Decode(barcodeBitmap);
if(result2 != null)
{
MessageBox.Show(result2.Text);
}
//Zbar 读取二维码
//Bitmap pImg = MakeGrayscale3((Bitmap)barcodeBitmap);
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);
scanner.SetConfiguration(ZBar.SymbolType.QRCODE, ZBar.Config.Enable, 1);
List<ZBar.Symbol> symbols = new List<ZBar.Symbol>();
symbols = scanner.Scan((System.Drawing.Image)barcodeBitmap);
if (symbols != null && symbols.Count > 0)
{
string result = string.Empty;
symbols.ForEach(s => result += "条码内容:" + s.Data + " 条码质量:" + s.Quality + Environment.NewLine);
MessageBox.Show(result);
}
}
string txtMsg = "";
TextBlock tb = new TextBlock() { Text = txtMsg };
tb.Foreground = System.Windows.Media.Brushes.Red;
return tb;
}
标签:Open,Image,System,Drawing,new,net,grad,CV,Size
From: https://blog.51cto.com/u_16249230/7398269