目录
目前只能识别简单的结构的图片
先添加引用OpenCVSharp+zxing,上我写的代码
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.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);
}
/// <summary>
/// 二值化图像
/// </summary>
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);
}
/// <summary>
/// 闭运算
/// </summary>
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);
}
/// <summary>
/// 膨胀腐蚀
/// </summary>
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);
}
/// <summary>
/// 显示处理后的图片
/// </summary>
/// <param name="name">处理过程名称</param>
/// <param name="srcImage">图片盒子</param>
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];
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();
}
//找出面积最大的轮廓
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);
}
/// <summary>
/// 解析条形码图片
/// </summary>
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();
}
截图出来的条形码进行灰度处理
/// <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;