用DIPlib测量垫圈

我正在使用opencv来测量垫圈的尺寸,以达到排序的目的。但是OpenCV不够精确,这就是为什么我想把我的代码从OpenCV迁移到DIPlib。下面的代码我测量了以下标准。

外径、孔径、偏心率、毛刺。

如何用DIPlib找到这些标准?

这是一个示例图像。

enter image description here

这是测量上述标准的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)
}

解决方案:

这个问题与 这个.

在你开始处理图像之前,有两件事你应该试着去做,以改善你的设置。

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

  2. 看起来我可以看到物体的一面(图片顶部的中间灰色区域)。除非物体在那里真的有一个锥形的边缘,这很可能是因为物体在视野中没有居中。使用较长的焦距可能会缓解一些这种情况。结果是,我们将不知道要测量哪个边缘,物体是包括灰色区域,还是不包括?

一旦我们进入测量,我们可以用DIPlib复制你在OpenCV中做的一些处理,通过追踪轮廓作为多边形并进行多边形测量。这不一定会产生比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 (因为你要测量面罩本身)。

给TA打赏
共{{data.count}}人
人已打赏
未分类

将外部应用连接到运行在kubernetes中的数据库?

2022-9-8 8:37:36

未分类

django rest框架,无效的json列表。

2022-9-8 8:37:38

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索