【问题标题】:How to detect and measure(fitEllipse) objects on the SEM image using OpenCV?如何使用 OpenCV 在 SEM 图像上检测和测量(fitEllipse)对象?
【发布时间】:2019-02-09 08:39:13
【问题描述】:

我有大约 30 张这样的 SEM(扫描电子显微镜)图像:

您看到的是玻璃基板上的光刻胶柱。 我想做的是得到 x 和 y 方向的平均直径以及 x 和 y 方向的平均周期。

现在,与其手动进行所有测量,我想知道是否有办法使用 python 和 opencv 实现自动化

编辑: 我尝试了以下代码,它似乎正在检测圆圈但我真正需要的是椭圆,因为我需要 x 和 y 方向的直径。

...我还不太明白如何获得秤?

import numpy as np
import cv2
from matplotlib import pyplot as plt

img = cv2.imread("01.jpg",0)
output = img.copy()

edged = cv2.Canny(img, 10, 300)
edged = cv2.dilate(edged, None, iterations=1)
edged = cv2.erode(edged, None, iterations=1)



# detect circles in the image
circles = cv2.HoughCircles(edged, cv2.HOUGH_GRADIENT, 1.2, 100)


# ensure at least some circles were found
if circles is not None:
    # convert the (x, y) coordinates and radius of the circles to integers
    circles = np.round(circles).astype("int")

    # loop over the (x, y) coordinates and radius of the circles
    for (x, y, r) in circles[0]:
        print(x,y,r)
        # draw the circle in the output image, then draw a rectangle
        # corresponding to the center of the circle
        cv2.circle(output, (x, y), r, (0, 255, 0), 4)
        cv2.rectangle(output, (x - 5, y - 5), (x + 5, y + 5), (0, 128, 255), -1)

    # show the output image
    plt.imshow(output, cmap = 'gray', interpolation = 'bicubic')
    plt.xticks([]), plt.yticks([])  # to hide tick values on X and Y axis
    plt.figure()
    plt.show()

灵感来源:https://www.pyimagesearch.com/2014/07/21/detecting-circles-images-using-opencv-hough-circles/

【问题讨论】:

  • 一些预处理可能会有所帮助。首先,我会切断底部的文本区域。找出所有明亮的大斑点。将图像划分为 roi,使得每个 roi 只包含一个 blob。丢弃包含部分 blob 的 roi(即 blob 靠近边缘的位置)。对剩余的 ROI 进行进一步分析。 (哦,不使用 JPEG 作为输入图像的荣誉)
  • 既然你提到了椭圆,你可以在柱子的轮廓上做cv2.fitEllipse

标签: python image opencv image-processing


【解决方案1】:

我很少发现 Hough 对现实世界的应用有用,因此我宁愿走去噪、分割和椭圆拟合的道路。

对于去噪,选择非局部均值 (NLM)。对于分割——只看图像——我想出了一个包含三类的高斯混合模型:一类用于背景,二类用于对象(漫反射和镜面反射分量)。在这里,混合模型本质上是通过三个高斯函数对灰度图像直方图的形状进行建模(如Wikipedia mixture-histogram gif 所示)。有兴趣的读者转至Wikipedia article

最后的椭圆拟合只是一个基本的 OpenCV 工具。

在 C++ 中,但类似于 OpenCV-Python

#include "opencv2/ml.hpp"
#include "opencv2/photo.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
void gaussianMixture(const cv::Mat &src, cv::Mat &dst, int nClasses )
{
    if ( src.type()!=CV_8UC1 )
        CV_Error(CV_StsError,"src is not 8-bit grayscale");

    // reshape
    cv::Mat samples( src.rows * src.cols, 1, CV_32FC1 );
    src.convertTo( cv::Mat( src.size(), CV_32FC1, samples.data ), CV_32F );

    cv::Mat labels;
    cv::Ptr<cv::ml::EM> em = cv::ml::EM::create();
    em->setClustersNumber( nClasses );
    em->setTermCriteria( cv::TermCriteria(CV_TERMCRIT_ITER, 4, 0.0 ) );
    em->trainEM( samples );

    if ( dst.type()!=CV_8UC1 || dst.size()!=src.size() )
        dst = cv::Mat( src.size(),CV_8UC1 );
    for(int y=0;y<src.rows;++y)
    {
        for(int x=0;x<src.cols;++x)
        {
            dst.at<unsigned char>(y,x) = em->predict( src.at<unsigned char>(y,x) );
        }
    }
}
void automate()
{
    cv::Mat input = cv::imread( /* input image in color */,cv::IMREAD_COLOR);
    cv::Mat inputDenoised;
    cv::fastNlMeansDenoising( input, inputDenoised, 8.0, 5, 17 );
    cv::Mat gray;
    cv::cvtColor(inputDenoised,gray,cv::COLOR_BGR2GRAY );
    gaussianMixture(gray,gray,3 );

    typedef std::vector< std::vector< cv::Point > >  VecOfVec;
    VecOfVec contours;
    cv::Mat objectPixels = gray>0;
    cv::findContours( objectPixels, contours, cv::RETR_LIST, cv::CHAIN_APPROX_NONE );
    cv::Mat inputcopy; // for drawing of ellipses
    input.copyTo( inputcopy );
    for ( size_t i=0;i<contours.size();++i )
    {
        if ( contours[i].size() < 5 )
            continue;
        cv::drawContours( input, VecOfVec{contours[i]}, -1, cv::Scalar(0,0,255), 2 );
        cv::RotatedRect rect = cv::fitEllipse( contours[i] );
        cv::ellipse( inputcopy, rect, cv::Scalar(0,0,255), 2 );
    }
}

我应该在绘制椭圆之前清理非常小的轮廓(在上排第二个)(大于最小 5 个点)。

* 编辑 * 添加了没有降噪和查找轮廓部分的 Python 预测器。学习模型后,预测时间约为1.1秒

img = cv.imread('D:/tmp/8b3Lm.jpg', cv.IMREAD_GRAYSCALE )

class Predictor :
    def train( self, img ):
        self.em = cv.ml.EM_create()
        self.em.setClustersNumber( 3 )
        self.em.setTermCriteria( ( cv.TERM_CRITERIA_COUNT,4,0 ) )
        samples = np.reshape( img, (img.shape[0]*img.shape[1], -1) ).astype('float')
        self.em.trainEM( samples )

    def predict( self, img ):
        samples = np.reshape( img, (img.shape[0]*img.shape[1], -1) ).astype('float')
        labels = np.zeros( samples.shape, 'uint8' )
        for i in range ( samples.shape[0] ):
            retval, probs = self.em.predict2( samples[i] )
            labels[i] = retval[1] * (255/3) # make it [0,255] for imshow
        return np.reshape( labels, img.shape )

predictor = Predictor()

predictor.train( img )
t = time.perf_counter()
predictor.train( img )
t = time.perf_counter() - t
print ( "train %s s" %t )

t = time.perf_counter()
labels = predictor.predict( img )
t = time.perf_counter() - t
print ( "predict %s s" %t )

cv.imshow( "prediction", labels  )
cv.waitKey( 0 )

【讨论】:

  • 不错。您能否添加一些有关gaussianMixture 工作原理的信息? (顺便说一句,输入是灰度的,所以你可以直接阅读它并跳过cvtColor——你不会从电子显微镜中得到颜色)。
  • @DanMašek,我想画彩色,所以无论哪种方式,我都必须使用cvtColor :)
  • 对... doh :D 感谢您提供额外的信息。我想在这种情况下,这类似于 k-means 聚类? (稍后我将不得不更详细地阅读它)
  • 很好的答案,但是你能不能同时提取 x 和 y 方向的椭圆长轴和短轴?
  • @mainactual 非常感谢!!抱歉回复晚了,不知怎的错过了你的评论...
【解决方案2】:

我会使用来自 openCV 的 HoughCircles 方法。它会给你图像中的所有圆圈。这样就很容易计算出每个圆的半径和位置了。

看:https://docs.opencv.org/3.4/d4/d70/tutorial_hough_circle.html

【讨论】:

  • 感谢您的留言!我会看看。 :)
  • 你知道如何检测椭圆吗?
  • skimage.transform 包有一个 hough_ellipse 函数,但我从未使用过。
【解决方案3】:

我首先在 OpenCV (Python) 中使用cv2.ml.EM 分割图像,它的成本约为13 s。如果只是fitEllipse 在脱粒图像的轮廓上,则花费5 ms,结果可能不那么准确。只是一个权衡。


详情:

  1. 转换成灰度并脱粒

  2. 变形去噪

  3. 寻找外部轮廓

  4. 拟合椭圆


代码:

#!/usr/bin/python3
# 2019/02/13 
# https://stackoverflow.com/a/54604608/54661984

import cv2
import numpy as np

fpath = "sem.png"
img = cv2.imread(fpath)

## Convert into grayscale and threshed it
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
th, threshed = cv2.threshold(gray, 120, 255, cv2.THRESH_BINARY)

## Morph to denoise
threshed = cv2.dilate(threshed, None)
threshed = cv2.erode(threshed, None)

## Find the external contours
cnts = cv2.findContours(threshed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)[-2]
cv2.drawContours(img, cnts, -1, (255, 0, 0), 2, cv2.LINE_AA)

## Fit ellipses
for cnt in cnts:
    if cnt.size < 10 or cv2.contourArea(cnt) < 100:
        continue

    rbox = cv2.fitEllipse(cnt)
    cv2.ellipse(img, rbox, (255, 100, 255), 2, cv2.LINE_AA)

## This it
cv2.imwrite("dst.jpg", img)

【讨论】:

  • 感谢您的回答。你能提取出长椭圆轴和短椭圆轴吗?
猜你喜欢
  • 2018-06-16
  • 1970-01-01
  • 2018-07-18
  • 2022-07-28
  • 1970-01-01
  • 2013-01-23
  • 1970-01-01
  • 2013-01-24
  • 2020-04-18
相关资源
最近更新 更多