C# OpenCvSharp标定物体,计算DPI。
·
/// <summary>
/// 1cm×1cm小方块标定工具类(OpenCvSharp4,标定函数参数为图片路径)
/// </summary>
public class SquareCalibrationTool
{
// 常量定义:可根据实际场景调整
private const int ThresholdValue = 127; // 二值化阈值
private const int CannyThresh1 = 50; // Canny低阈值
private const int CannyThresh2 = 150; // Canny高阈值
private const double SquareActualSizeCm = 1.0; // 标定物实际尺寸:1cm×1cm
private const double alpha=1; //对比度
private const double beta=5; //亮度
private const int MorphKernelSize = 3; // 形态学核大小
private const int GaussianKernelSize = 5; // 高斯核大小
/// <summary>
/// 标定1cm×1cm正方形,计算像素与厘米的比例(逐个显示轮廓+颜色标注)
/// </summary>
/// <param name="imagePath">输入图像路径</param>
/// <param name="outDir">过程图片保存目录</param>
/// <param name="calibratedImage">输出的标定结果图</param>
/// <param name="pixelToCmRatio">输出的像素转厘米比例</param>
/// <returns>是否标定成功</returns>
public static bool CalibrateSquare(string imagePath, string outDir, out Mat calibratedImage, out double dpi)
{
// 初始化输出参数
calibratedImage = null;
dpi = 0;
Point[] squareContour = null;
// 声明所有需要的Mat对象(扁平化管理)
Mat inputImage = null;
Mat grayImage = null;
Mat blurredImage = null;
Mat binaryImage = null;
Mat morphImage = null;
Mat edgeImage = null;
Mat edgeImageClone = null;
try
{
#region 1. 基础校验与文件处理
if (!File.Exists(imagePath))
{
Console.WriteLine($"错误:原始图片不存在,路径:{imagePath}");
return false;
}
if (!Directory.Exists(outDir))
{
Directory.CreateDirectory(outDir);
Console.WriteLine($"已创建保存目录:{outDir}");
}
inputImage = Cv2.ImRead(imagePath, ImreadModes.Color);
if (inputImage.Empty())
{
Console.WriteLine($"错误:无法读取图片(格式不支持或文件损坏),路径:{imagePath}");
return false;
}
Console.WriteLine($"成功读取图片:{imagePath}");
SaveProcessImage(inputImage, Path.Combine(outDir, "0_原图.jpg"));
#endregion
#region 2. 图像预处理(平铺步骤)
// 步骤1:转灰度图
grayImage = new Mat();
Cv2.CvtColor(inputImage, grayImage, ColorConversionCodes.BGR2GRAY);
Cv2.ConvertScaleAbs(grayImage, grayImage, alpha, beta);
SaveProcessImage(grayImage, Path.Combine(outDir, "1_灰度图.jpg"));
// 步骤2:高斯降噪
blurredImage = new Mat();
Cv2.GaussianBlur(grayImage, blurredImage, new Size(GaussianKernelSize, GaussianKernelSize), 1.5);
SaveProcessImage(blurredImage, Path.Combine(outDir, "2_高斯降噪.jpg"));
// 步骤3:二值化(反相)
binaryImage = new Mat();
Cv2.Threshold(blurredImage, binaryImage, ThresholdValue, 255, ThresholdTypes.BinaryInv);
SaveProcessImage(binaryImage, Path.Combine(outDir, "3_二值化.jpg"));
// 步骤4:形态学去噪
morphImage = new Mat();
Mat kernel = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(MorphKernelSize, MorphKernelSize));
Cv2.MorphologyEx(binaryImage, morphImage, MorphTypes.Open, kernel, iterations: 1);
Cv2.MorphologyEx(morphImage, morphImage, MorphTypes.Close, kernel, iterations: 1);
SaveProcessImage(morphImage, Path.Combine(outDir, "4_形态学去噪.jpg"));
// 步骤5:Canny边缘检测
edgeImage = new Mat();
Cv2.Canny(morphImage, edgeImage, CannyThresh1, CannyThresh2);
SaveProcessImage(edgeImage, Path.Combine(outDir, "5_边沿检测.jpg"));
#endregion
#region 3. 轮廓检测
edgeImageClone = edgeImage.Clone();
Point[][] contours;
HierarchyIndex[] hierarchy;
Cv2.FindContours(
edgeImageClone,
out contours,
out hierarchy,
RetrievalModes.External,
ContourApproximationModes.ApproxSimple,
new Point(0, 0)
);
#endregion
#region 4. 筛选正方形轮廓
// 第一步:找到面积最大的轮廓
double maxArea = 0;
Point [] maxAreaContour = null;
foreach (var contour in contours)
{
double area = Cv2.ContourArea(contour);
if (area > maxArea)
{
maxArea = area;
maxAreaContour = contour;
}
}
// 第二步:对最大面积轮廓做形状筛选
if (maxAreaContour != null)
{
calibratedImage = inputImage.Clone();
RotatedRect rotatedRect = Cv2.MinAreaRect(maxAreaContour);
float pixelWidth = rotatedRect.Size.Width;
float pixelHeight = rotatedRect.Size.Height;
float avgPixelSize = (pixelWidth + pixelHeight) / 2f;
// ===================== 通用配置:修改此处即可切换标定尺寸 =====================
float dpiCoefficient = 2.54f / (float)SquareActualSizeCm;
dpi = avgPixelSize * dpiCoefficient;
// ==========================================================================
Point2f[] vertices = Cv2.BoxPoints(rotatedRect);
Point[] points = Array.ConvertAll(vertices, p => new Point((int)p.X, (int)p.Y));
Cv2.Polylines(calibratedImage, new[] { points }, true, Scalar.Red, 2);
// 标注位置与样式
string dpiText = $"DPI:{dpi:F2}";
Size textSize = Cv2.GetTextSize(dpiText, HersheyFonts.HersheySimplex, 0.6, 2, out int baseline);
Point textPos = new Point((int)rotatedRect.Center.X- textSize.Width/2, rotatedRect.Center.Y);
Cv2.PutText(calibratedImage, dpiText, textPos, HersheyFonts.HersheySimplex, 0.6, Scalar.Red, 2);
SaveProcessImage(calibratedImage, Path.Combine(outDir, "6_标定结果图.jpg"));
}
#endregion
return true;
}
catch (Exception ex)
{
Console.WriteLine($"标定失败:{ex.Message}");
return false;
}
finally
{
// 释放所有Mat资源
inputImage?.Release();
grayImage?.Release();
blurredImage?.Release();
binaryImage?.Release();
morphImage?.Release();
edgeImage?.Release();
edgeImageClone?.Release();
}
}
#region 工具方法:保存图像(带异常处理)
/// <summary>
/// 保存OpenCvSharp的Mat图像到指定路径
/// </summary>
/// <param name="image">要保存的图像</param>
/// <param name="fileName">文件名(按步骤命名)</param>
private static void SaveProcessImage(Mat image, string filePath)
{
try
{
Cv2.ImWrite(filePath, image);
Console.WriteLine($"过程图片已保存:{filePath}");
}
catch (Exception ex)
{
Console.WriteLine($"图片保存失败【{filePath}】:{ex.Message}");
}
}
#endregion
}
更多推荐
所有评论(0)