前言
机器视觉是工业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. **实时性优化**:工业检测需要高帧率,需要优化算法性能。
机器视觉入门有一定难度,但掌握后能为项目带来显著价值。建议从简单的图像处理算法开始,逐步深入到深度学习等高级技术。