简体   繁体   English

如何使用 python opencv 可靠地仅选择制作液滴图像的外轮廓?

[英]How can I reliably choose only the outer contour of a maksed droplet image with python opencv?

I am writing a program which needs to detect the outer contour of a droplet and fit an ellipse to that shape.我正在编写一个程序,该程序需要检测液滴的外轮廓并将椭圆拟合到该形状。

I have a setup that has been working fine:我有一个运行良好的设置:

  1. canny edge detection精明的边缘检测
  2. find contours寻找轮廓
  3. pick longest contour选择最长的轮廓
  4. fit ellipse拟合椭圆

But now the process needs to include a mask for the syringe that dispenses the droplet, which splits the outer contour in half.但是现在这个过程需要包括一个用于分配液滴的注射器的面罩,它将外轮廓分成两半。 I know how to mask the detected edges from the array returned from canny but I don't know how to proceed from there.我知道如何掩盖从 canny 返回的数组中检测到的边缘,但我不知道如何从那里开始。

边缘检测和遮罩后的图像

I need to use the two outer contours to fit the ellipse but I don' know how to reliably extract those.我需要使用两个外部轮廓来拟合椭圆,但我不知道如何可靠地提取它们。

Minimal working code:最小的工作代码:

from typing import Tuple
import cv2
import numpy as np

def evaluate_droplet(img, y_base, mask: Tuple[int,int,int,int] = None):
    # crop img from baseline down (contains no useful information)
    crop_img = img[:y_base,:]
    shape = img.shape
    height = shape[0]
    width = shape[1]
    # calculate thrresholds
    thresh_high, thresh_im = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
    thresh_low = 0.5*thresh_high
    bw_edges = cv2.Canny(crop_img, thresh_low, thresh_high)
    
    # block detection of syringe
    if (not mask is None):
        x,y,w,h = mask
        bw_edges[y:y+h, x:x+w] = 0
        cv2.imshow('bw',bw_edges)
        cv2.waitKey(0)


# for testing purposes:
if __name__ == "__main__":
    im = cv2.imread('untitled1.png', cv2.IMREAD_GRAYSCALE)
    im = np.reshape(im, im.shape + (1,) )
    (h,w,d) = np.shape(im)
    try:
        drp = evaluate_droplet(im, 250, (int(w/2-40), 0, 80, h))
    except Exception as ex:
        print(ex)
    cv2.imshow('Test',im)
    cv2.waitKey(0)

the image used:使用的图像:
测试图像

I solved it like this for now:我现在这样解决了:

Calculate the area of the bounding rect for each contour and pick largest two计算每个轮廓的边界矩形的面积并选择最大的两个

def evaluate_droplet(img, y_base, mask: Tuple[int,int,int,int] = None):
    # crop img from baseline down (contains no useful information)
    crop_img = img[:y_base,:]
    shape = img.shape
    height = shape[0]
    width = shape[1]
    # calculate thrresholds
    thresh_high, thresh_im = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
    thresh_low = 0.5*thresh_high
    bw_edges = cv2.Canny(crop_img, thresh_low, thresh_high)
    
    # block detection of syringe
    if (not mask is None):
        x,y,w,h = mask
        bw_edges[y:y+h, x:x+w] = 0

    contours, hierarchy = cv2.findContours(bw_edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
    if len(contours) == 0:
        print("no contour found!")

    cont_area_list = []
    # match contours with their bounding rect area
    for cont in contours:
        x,y,w,h = cv2.boundingRect(cont)
        cont_area_list.append((cont, w*h))
    
    # sort combined list by area
    cont_areas_sorted = sorted(cont_area_list, key=lambda item: item[1])

    # largest 2 contours, assumes mask splits largest contour in the middle
    if not mask is None:
        largest_conts = [elem[0] for elem in cont_areas_sorted[-2:]]
        # merge largest 2 contorus into one for handling purposes
        contour = np.concatenate((largest_conts[0], largest_conts[1]))
    # if no mask is used use only single largest contour
    else:
        contour = cont_areas_sorted[-1][0]
    
    # display bounding rect of final contour
    x,y,w,h = cv2.boundingRect(contour)
    bw_edges = cv2.rectangle(bw_edges, (x,y), (x+w,y+h), (255,255,255))
    cv2.imshow('bw',bw_edges)
    cv2.waitKey(0)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM