1

我正在使用 opencv 测量垫圈尺寸以进行分类。但是 OpenCV 不够精确,这就是我想将代码从 OpenCV 迁移到 DIPlib 的原因。使用以下代码,我正在测量以下标准:

外径、孔径、偏心、毛刺

如何使用 DIPlib 找到这些标准?

这是一个示例图像:

在此处输入图像描述

这是衡量上述标准的 OpenCV 代码:

blur(openCvImage, openCvImage, Size(3, 3));
threshold(openCvImage, thresh_output, parameter.thresh1, parameter.thresh1 * 3, THRESH_BINARY_INV);
findContours(thresh_output, contours, hierarchy, RETR_LIST, CHAIN_APPROX_SIMPLE);
cvtColor(openCvImage, openCvImage, COLOR_GRAY2RGB);

if (contours.size() == 2)
{
    vector<Moments> mu(contours.size());//contours
    vector<Point2f> mc(contours.size());//centroid
    vector<RotatedRect> minRect(contours.size());//min rectangle

    // draw contours and draw point centers of inner and outter circles and find inner and outer perimeter
    for (int i = 0; i < contours.size(); i++)
    {
        mu[i] = moments(contours[i], false);// get the moments
        mc[i] = Point2f(mu[i].m10 / mu[i].m00, mu[i].m01 / mu[i].m00);// get the centroid of figures.
        drawContours(openCvImage, contours, i, color, 2, 8, hierarchy, 0, Point());//draw contours
        circle(openCvImage, mc[i], 2, color, -1, 8, 0);//Draw point centroid of the circles
        minRect[i] = minAreaRect(contours[i]);//find min fitted rectangle to circles
        diameter[i] = arcLength(contours[i], 1) / (M_PI);//find diameter of the washer and washer hole(R=perimeter/pi)
        if (minRect[i].size.width < minRect[i].size.height) { swap(minRect[i].size.width, minRect[i].size.height); }//sort the values
        //a=shortest diameter b=longest diameter  sqrt(b2-a2)/b if b=a equation=0 if a goes to 0 equation=1 eliptic is between 0 an 1 (*100)
        eliptic[i] = ((sqrt(pow((minRect[i].size.width / 2), 2) - pow((minRect[i].size.height / 2), 2))) / (minRect[i].size.width / 2)) * 100;
    }
    burrdistance = pointPolygonTest(contours[0], mc[0], 1);//find the distance from centroid to burr
    eccentricity = norm(mc[0] - mc[1]);//find the distance between centroid of the circles
    circle(openCvImage, mc[0], burrdistance, (0, 255, 0), 1, 8, 0);//making circle from centroid to burr
    burrpercentage = ((diameter[0] / 2) - burrdistance) / (diameter[0] / 2) * 100;//(radius-burrdistance)/radius)
}
4

1 回答 1

1

这个问题与另一个有关。

在开始处理图像之前,您应该尝试做两件事来改进设置:

  1. 背景太亮了。这些像素是饱和的。当 CCD 具有饱和像素时,附近的像素会产生比应有的更高的值。这种效果称为绽放。它会导致您的对象看起来比实际更小。要么降低光强度,要么缩短曝光时间,要么关闭光圈,直到背景像素刚好低于它们的最大值。

  2. 看起来我可以看到对象的一侧(图片顶部的中间灰色区域)。除非对象实际上在那里有一个锥形边缘,否则这很可能是因为对象不在视野的中心。使用更长的焦点可能会减轻一些这种情况。结果是我们不知道要测量哪个边缘,对象是否包含灰色区域,还是不包含?

一旦我们开始测量,我们可以复制您在 OpenCV 中使用 DIPlib 执行的一些处理,方法是将轮廓跟踪为多边形并进行多边形测量。除了周长测量(OpenCV 总是高估)外,这不一定会产生比使用 ​​OpenCV 更好的结果。您可以在现有代码中根据面积而不是周长计算直径,以获得更精确的结果。

测量也是minRect不精确的,因为它受单个像素的影响,一些噪声会引入偏差。相反,将椭圆拟合到多边形,并在elliptic测量中使用椭圆的直径。

同样,burrdistance测量给出了质心到轮廓中最近像素的距离,这很容易受到噪声的影响,因此有偏差。burrpercentage取决于该值,因此也可能有偏差。我不确定这些测量应该提供什么,所以不会提出替代方案。但是考虑椭圆方差度量来量化轮廓的粗糙度(它量化了到最佳拟合椭圆的距离的方差)。

如果多边形测量不够精确,您可以在图像中添加灰度信息以获得更精确的测量。这是这样做的 DIPlib 代码:

#include "diplib.h"
#include "diplib/simple_file_io.h"
#include "diplib/mapping.h"
#include "diplib/binary.h"
#include "diplib/morphology.h"
#include "diplib/measurement.h"

int main() {
   double pixelSize = 0.001; // millimeters per pixel. This is just an example. You need to calibrate your image.
   dip::Image input = dip::ImageRead( "/Users/cris/tmp/washer.jpg" );
   input.SetPixelSize( pixelSize * dip::Units::Millimeter() );
   double low = 120;
   double high = 170; // adjust these values according to illumination
   input = dip::ErfClip( input, low, high, "both" ); // This removes noise and edge variability.
   input = ( input - low ) / ( high - low ); // normalize

   // Create masks images that separate hole from object, so we can measure them independently:
   dip::Image hole = input > 0.5;
   hole = dip::BinaryAreaOpening( dip::EdgeObjectsRemove( hole ), 1000 );
   dip::Dilation( hole, hole, { 10 } ); // Add a margin so we include the full edge
   dip::Image washer = ( input <= 0.5 ) | hole;
   dip::Dilation( washer, washer, { 10 } ); // Add a margin so we include the full edge

   // Measure hole
   dip::MeasurementTool measurementTool;
   dip::Image holeLabel = dip::Convert( hole, dip::DT_UINT8 ); // instead of labeling, all regions have object ID = 1
   auto holeMsr = measurementTool.Measure( holeLabel, input, { "Mass", "Gravity", "GreyDimensionsEllipsoid" } );
   double holeArea = holeMsr[ 1 ][ "Mass" ][ 0 ] * pixelSize * pixelSize;
   double holeDiameter = 2 * std::sqrt( holeArea / dip::pi );
   double holeCentroidX = holeMsr[ 1 ][ "Gravity" ][ 0 ];
   double holeCentroidY = holeMsr[ 1 ][ "Gravity" ][ 1 ];
   double holeMajorAxis = holeMsr[ 1 ][ "GreyDimensionsEllipsoid" ][ 0 ];
   double holeMinorAxis = holeMsr[ 1 ][ "GreyDimensionsEllipsoid" ][ 1 ];

   // Measure washer
   input = 1.0 - input;
   input.At( hole ) = 1.0;
   washer.Convert( dip::DT_UINT8 ); // instead of labeling, all regions have object ID = 1
   auto washerMsr = measurementTool.Measure( washer, input, { "Mass", "Gravity", "GreyDimensionsEllipsoid" } );
   double washerArea = washerMsr[ 1 ][ "Mass" ][ 0 ] * pixelSize * pixelSize;
   double washerDiameter = 2 * std::sqrt( washerArea / dip::pi );
   double washerCentroidX = washerMsr[ 1 ][ "Gravity" ][ 0 ];
   double washerCentroidY = washerMsr[ 1 ][ "Gravity" ][ 1 ];
   double washerMajorAxis = washerMsr[ 1 ][ "GreyDimensionsEllipsoid" ][ 0 ];
   double washerMinorAxis = washerMsr[ 1 ][ "GreyDimensionsEllipsoid" ][ 1 ];

   // Output measurements
   std::cout << "washer area = " << washerArea << " mm², diameter = " << washerDiameter
             << " mm, major diameter = " << washerMajorAxis << " mm, minor diameter = " << washerMinorAxis
             << " mm, centroid = (" << washerCentroidX << ", " << washerCentroidY << ") mm\n";
   std::cout << "hole area = " << holeArea << " mm², diameter = " << holeDiameter
             << " mm, major diameter = " << holeMajorAxis << " mm, minor diameter = " << holeMinorAxis
             << " mm, centroid = (" << holeCentroidX << ", " << holeCentroidY << ") mm\n";
}

请注意,上述代码的准确性(偏差)受​​灰色边缘区域的影响。直径是根据面积来测量的,椭圆的长径和短径是根据椭圆与形状的拟合来测量的。

这是输出:

washer area = 0.568496 mm², diameter = 0.850783 mm, major diameter = 0.853937 mm, minor diameter = 0.84772 mm, centroid = (0.737456, 0.474875) mm
hole area = 0.0417281 mm², diameter = 0.230499 mm, major diameter = 0.230843 mm, minor diameter = 0.230167 mm, centroid = (0.73646, 0.470806) mm

如果您不想使用灰度值测量,可以与上述类似,但使用等效的二元测量:“Size”、“Center”和“DimensionsEllipsoid”。“Size”考虑了像素大小,因此不需要像“Mass”那样进行乘法运算。在这种情况下,您不需要将灰度图像传递给measurementTool.Measure,也不应该应用于dip::Dilation蒙版(因为您将自己测量蒙版)。

于 2020-04-23T17:08:07.550 回答