技术学习··41 分钟阅读

机器视觉入门:质量检测自动化

探索机器视觉在工业自动化中的应用,学习图像处理基础、OpenCV应用以及质量检测系统的开发

机器视觉OpenCV图像处理质量检测工业自动化

前言

机器视觉是工业4.0时代的重要技术,广泛应用于产品质量检测、尺寸测量、缺陷识别、装配验证等领域。作为自动化工程师,掌握机器视觉的基础原理和C#开发方法,能为项目带来更高的附加值。

机器视觉系统组成

硬件组成

```

┌─────────────┐

│ 工业相机 │ ← 图像采集(CCD/CMOS传感器)

└──────┬──────┘

┌──────▼──────┐

│ 镜头 │ ← 成像光学(焦距、光圈、畸变校正)

└──────┬──────┘

┌──────▼──────┐

│ 光源 │ ← LED光源(环形光、条光、背光等)

└──────┬──────┘

┌──────▼──────┐

│ 图像采集卡│ ← 数据传输

└──────┬──────┘

┌──────▼──────┐

│ 工控机 │ ← 图像处理与决策

└────────────┘

```

软件流程

1. **图像采集**:从相机获取原始图像

2. **预处理**:去噪、校正、增强

3. **特征提取**:定位、测量、识别

4. **结果判断**:合格/不合格判定

5. **结果输出**:显示、存储、信号输出

OpenCV基础应用

C#环境配置

使用EmguCV(OpenCV的.NET封装)进行开发。

```csharp

// 安装NuGet包

// Install-Package Emgu.CV

// Install-Package Emgu.CV.runtime.windows

using Emgu.CV;

using Emgu.CV.CvEnum;

using Emgu.CV.Structure;

public class VisionProcessor

{

private Mat _sourceImage;

private Mat _grayImage;

private Mat _binaryImage;

public void LoadImage(string filePath)

{

_sourceImage = CvInvoke.Imread(filePath, ImreadModes.Color);

CvInvoke.CvtColor(_sourceImage, _grayImage, ColorConversion.Bgr2Gray);

}

}

```

1. 图像预处理

```csharp

// 去噪处理(高斯模糊)

public Mat DenoiseImage(Mat input)

{

Mat output = new Mat();

CvInvoke.GaussianBlur(input, output, new Size(5, 5), 0);

return output;

}

// 图像增强(直方图均衡化)

public Mat EnhanceImage(Mat grayImage)

{

Mat enhanced = new Mat();

CvInvoke.EqualizeHist(grayImage, enhanced);

return enhanced;

}

// 二值化处理

public Mat BinaryThreshold(Mat grayImage)

{

Mat binary = new Mat();

CvInvoke.Threshold(grayImage, binary, 127, 255, ThresholdType.BinaryInv);

return binary;

}

```

2. 边缘检测

```csharp

// Canny边缘检测

public Mat DetectEdges(Mat grayImage)

{

Mat edges = new Mat();

CvInvoke.Canny(grayImage, edges, 50, 150);

return edges;

}

// 轮廓查找

public MCvContour[] FindContours(Mat binaryImage)

{

Mat hierarchy = new Mat();

MCvContour[] contours;

CvInvoke.FindContours(

binaryImage,

out contours,

hierarchy,

RetrType.External,

ChainApproxMethod.ChainApproxSimple

);

return contours;

}

```

3. 几何形状检测

```csharp

// 检测圆形

public CircleF[] DetectCircles(Mat grayImage)

{

CircleF[] circles = CvInvoke.HoughCircles(

grayImage,

HoughType.Gradient,

1, // 累加器分辨率的反比

100, // 圆心间最小距离

100, // 更高阈值(Canny边缘检测)

30, // 检测圆中心的累加器阈值

10, // 最小半径

100 // 最大半径

);

return circles;

}

// 检测矩形

public Rectangle[] DetectRectangles(Mat binaryImage)

{

var contours = FindContours(binaryImage);

var rectangles = new List<Rectangle>();

foreach (var contour in contours)

{

var approx = new Mat();

CvInvoke.ApproxPolyDP(contour, approx, 0.02 * CvInvoke.ArcLength(contour, true), true);

if (approx.Rows == 4 && CvInvoke.ContourArea(approx) > 500)

{

var rect = CvInvoke.BoundingRectangle(approx);

rectangles.Add(rect);

}

}

return rectangles.ToArray();

}

```

实际应用案例

案例1:零件尺寸测量

```csharp

public class DimensionMeasurement

{

// 测量两个圆的中心距

public double MeasureCenterDistance(Mat image)

{

// 1. 预处理

Mat gray = ConvertToGray(image);

Mat binary = BinaryThreshold(gray);

// 2. 查找圆形

var circles = CvInvoke.HoughCircles(

gray,

HoughType.Gradient,

1, 100, 100, 30, 10, 100

);

if (circles.Length < 2)

{

throw new Exception("未找到足够的圆形特征");

}

// 3. 计算中心距(假设标定系数为0.1mm/pixel)

var circle1 = circles[0];

var circle2 = circles[1];

double pixelDistance = Math.Sqrt(

Math.Pow(circle1.Center.X - circle2.Center.X, 2) +

Math.Pow(circle1.Center.Y - circle2.Center.Y, 2)

);

double mmDistance = pixelDistance * 0.1; // 像素转毫米

return mmDistance;

}

// 绘制测量结果

public Mat DrawMeasurement(Mat image, CircleF[] circles)

{

var result = image.Clone();

for (int i = 0; i < circles.Length; i++)

{

var circle = circles[i];

CvInvoke.Circle(result, Point.Round(circle.Center), (int)circle.Radius, new MCvScalar(0, 255, 0), 2);

CvInvoke.PutText(

result,

$"C{i + 1}",

Point.Round(circle.Center),

FontFace.HersheySimplex,

0.7,

new MCvScalar(255, 255, 255),

2

);

}

// 如果有两个圆,绘制中心距

if (circles.Length >= 2)

{

var c1 = circles[0].Center;

var c2 = circles[1].Center;

CvInvoke.Line(result, Point.Round(c1), Point.Round(c2), new MCvScalar(255, 0, 0), 2);

double pixelDist = Math.Sqrt(Math.Pow(c1.X - c2.X, 2) + Math.Pow(c1.Y - c2.Y, 2));

CvInvoke.PutText(

result,

$"{pixelDist:F1}px",

Point.Round(new PointF((c1.X + c2.X) / 2, (c1.Y + c2.Y) / 2)),

FontFace.HersheySimplex,

0.6,

new MCvScalar(255, 0, 0),

2

);

}

return result;

}

}

```

案例2:表面缺陷检测

```csharp

public class DefectDetection

{

// 检测划痕

public bool DetectScratch(Mat image)

{

// 1. 转换为灰度图

Mat gray = new Mat();

CvInvoke.CvtColor(image, gray, ColorConversion.Bgr2Gray);

// 2. 使用形态学操作突出划痕

Mat kernel = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(15, 1), new Point(-1, -1));

Mat enhanced = new Mat();

CvInvoke.MorphologyEx(gray, enhanced, MorphOp.TopHat, kernel, new Point(-1, -1), 1);

// 3. 二值化

Mat binary = new Mat();

CvInvoke.Threshold(enhanced, binary, 50, 255, ThresholdType.Binary);

// 4. 查找细长轮廓(划痕特征)

var contours = CvInvoke.FindContoursAsArray(

binary,

RetrList,

ChainApproxMethod.ChainApproxSimple

);

foreach (var contour in contours)

{

var rect = CvInvoke.BoundingRectangle(contour);

double aspectRatio = (double)rect.Width / rect.Height;

// 细长形状(宽度远小于高度)可能是划痕

if (aspectRatio < 0.2 && rect.Width > 10)

{

return true;

}

}

return false;

}

// 检测凹坑

public bool DetectDent(Mat image)

{

// 1. 转换为HSV色彩空间

Mat hsv = new Mat();

CvInvoke.CvtColor(image, hsv, ColorConversion.Bgr2Hsv);

// 2. 提取亮度通道

Mat[] channels = new Mat[3];

CvInvoke.Split(hsv, out channels);

Mat vChannel = channels[2];

// 3. 高斯滤波平滑图像

Mat smooth = new Mat();

CvInvoke.GaussianBlur(vChannel, smooth, new Size(21, 21), 0);

// 4. 原图减去平滑图,突出局部暗斑

Mat diff = new Mat();

CvInvoke.Subtract(vChannel, smooth, diff);

// 5. 二值化并查找暗斑

Mat binary = new Mat();

CvInvoke.Threshold(diff, binary, 20, 255, ThresholdType.Binary);

var contours = CvInvoke.FindContoursAsArray(

binary,

RetrList,

ChainApproxMethod.ChainApproxSimple

);

foreach (var contour in contours)

{

double area = CvInvoke.ContourArea(contour);

if (area > 100) // 面积阈值

{

return true;

}

}

return false;

}

}

```

案例3:字符识别(OCR)

```csharp

public class TextRecognition

{

public string RecognizeText(Mat image)

{

// 1. 预处理:灰度化+二值化

Mat gray = new Mat();

CvInvoke.CvtColor(image, gray, ColorConversion.Bgr2Gray);

CvInvoke.Threshold(gray, gray, 0, 255, ThresholdType.Binary | ThresholdType.Otsu);

// 2. 查找文本区域

Mat kernel = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3, 3), new Point(-1, -1));

Mat opening = new Mat();

CvInvoke.MorphologyEx(gray, opening, MorphOp.Open, kernel, new Point(-1, -1), 1);

var contours = CvInvoke.FindContoursAsArray(opening, RetrList, ChainApproxMethod.ChainApproxSimple);

// 3. 过滤并排序文本块

var textRegions = contours

.Where(c => CvInvoke.ContourArea(c) > 100)

.OrderBy(c => CvInvoke.BoundingRectangle(c).Y)

.ThenBy(c => CvInvoke.BoundingRectangle(c).X)

.ToArray();

// 4. 提取每个字符(这里简化为直接使用OCR库)

string recognizedText = "";

foreach (var region in textRegions)

{

var rect = CvInvoke.BoundingRectangle(region);

Mat roi = new Mat(gray, rect);

// 这里应该调用OCR库(如Tesseract)

// string charText = TesseractRecognize(roi);

// recognizedText += charText;

recognizedText += "?"; // 简化处理

}

return recognizedText;

}

}

```

相机标定与坐标系转换

像素坐标转世界坐标

```csharp

public class CameraCalibration

{

private Matrix<float> _cameraMatrix; // 相机内参

private Matrix<float> _distCoeffs; // 畸变系数

// 标定(需要拍摄标定板多张图片)

public void Calibrate(List<Mat> calibrationImages)

{

// 实际项目中使用OpenCV的calibrateCamera函数

// 这里简化处理

_cameraMatrix = new Matrix<float>(3, 3);

_distCoeffs = new Matrix<float>(5, 1);

}

// 像素坐标转物理坐标(需要已知Z平面)

public PointF PixelToWorld(Point pixel, double zPlane)

{

// 逆投影变换

Matrix<float> invCamera = new Matrix<float>(_cameraMatrix.Rows, _cameraMatrix.Cols);

CvInvoke.Invert(_cameraMatrix, invCamera);

float[] pixelData = { pixel.X, pixel.Y, 1 };

Matrix<float> pixelMat = new Matrix<float>(pixelData);

Matrix<float> worldRay = invCamera * pixelMat;

// 假设在zPlane平面上

double t = zPlane / worldRay[2, 0];

double worldX = worldRay[0, 0] * t;

double worldY = worldRay[1, 0] * t;

return new PointF((float)worldX, (float)worldY);

}

}

```

上位机集成应用

```csharp

// 集成到WPF上位机

public class VisionInspectionViewModel : INotifyPropertyChanged

{

private string _status;

public string Status

{

get => _status;

set

{

_status = value;

OnPropertyChanged();

}

}

private bool _isQualified;

public bool IsQualified

{

get => _isQualified;

set

{

_isQualified = value;

OnPropertyChanged();

}

}

private Mat _capturedImage;

public Mat CapturedImage

{

get => _capturedImage;

set

{

_capturedImage = value;

OnPropertyChanged();

}

}

private readonly VisionProcessor _processor = new VisionProcessor();

public async Task InspectAsync()

{

Status = "检测中...";

IsQualified = false;

try

{

// 1. 采集图像

var image = await CaptureImageAsync();

CapturedImage = image;

// 2. 缺陷检测

var detector = new DefectDetection();

bool hasDefect = detector.DetectScratch(image) || detector.DetectDent(image);

IsQualified = !hasDefect;

Status = IsQualified ? "合格" : "不合格";

// 3. 输出控制信号

await ControlOutputAsync(IsQualified);

}

catch (Exception ex)

{

Status = $"检测失败: {ex.Message}";

}

}

private async Task<Mat> CaptureImageAsync()

{

// 调用相机SDK采集图像

return await Task.Run(() =>

{

// 实际实现:从相机采集

return new Mat();

});

}

private async Task ControlOutputAsync(bool isQualified)

{

// 控制IO信号:合格输出OK信号,不合格输出NG信号

await _plc.WriteBoolAsync("DB1.DBX0.0", isQualified);

await _plc.WriteBoolAsync("DB1.DBX0.1", !isQualified);

}

}

```

总结

机器视觉是高级工控技术:

1. **图像处理基础**:掌握OpenCV的基本操作(滤波、边缘检测、形态学)。

2. **算法选择**:根据检测对象选择合适的算法(边缘检测、模板匹配、深度学习)。

3. **光照控制**:良好的光照是成功的关键,合理的光源方案事半功倍。

4. **标定技术**:准确的标定是测量的基础。

5. **实时性优化**:工业检测需要高帧率,需要优化算法性能。

机器视觉入门有一定难度,但掌握后能为项目带来显著价值。建议从简单的图像处理算法开始,逐步深入到深度学习等高级技术。