如何使用opencv对图像进行矢量化?

How to vectorize an image using opencv?

我使用术语“向量化”是因为这是用来描述我正在写的过程的。我不知道它实际上叫什么,但我想做的是获取图像的元素并将它们分成不同的图像。

Here is an example picture I'm trying to "vectorize"

我想做的是(使用 opencv)将玉米芯与它所附着的绿色原料分开,并将每个玉米芯块分成它们自己的图像。

我试过的是:

def kmeansSegmentation(path_to_images, image_name, path_to_save_segments):
    img = cv2.imread(path_to_images+image_name)

    img_blur = cv2.GaussianBlur(img, (3,3), 0)
    img_gray = cv2.cvtColor(img_blur, cv2.COLOR_BGR2GRAY)

    img_reshaped = img_gray.reshape((-1, 3))
    img_reshaped = np.float32(img_reshaped)
    criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)


    K = 5
    attempts = 10
    ret,label,center=cv2.kmeans(img_reshaped,K,None,criteria,attempts,cv2.KMEANS_PP_CENTERS)
    center = np.uint8(center)
    res = center[label.flatten()]

    v = np.median(res)
    sigma=0.33
    lower = int(max(0, (1.0 - sigma) * v))
    upper = int(min(255, (1.0 + sigma) * v))
    edges = cv2.Canny(img_gray, lower, upper)

    contours, hierarchy = cv2.findContours(edges.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
    sorted_contours= sorted(contours, key=cv2.contourArea, reverse= True)
    mask = np.zeros(img.shape[:2], dtype=img.dtype)



    array_of_contour_areas = [cv2.contourArea(contour) for contour in contours]
    contour_avg = sum(array_of_contour_areas)/len(array_of_contour_areas)
    contour_var = sum(pow(x-contour_avg,2) for x in array_of_contour_areas) / len(array_of_contour_areas)
    contour_std = math.sqrt(contour_var)


    print("Saving segments", len(sorted_contours))
    for (i,c) in tqdm(enumerate(sorted_contours)):
        if (cv2.contourArea(c) > contour_avg-contour_std*2):
            x,y,w,h= cv2.boundingRect(c)
            cropped_contour= img[y:y+h, x:x+w]
            cv2.drawContours(mask, [c], 0, (255), -1)
            #tmp_image_name= image_name + "-kmeans-" + str(K) + str(random.random()) + ".jpg"
            #cv2.imwrite(path_to_save_segments+tmp_image_name, cropped_contour)


    result = cv2.bitwise_and(img, img, mask=mask)
    
    """
    scale_percent = 30 # percent of original size
    width = int(edges.shape[1] * scale_percent / 100)
    height = int(edges.shape[0] * scale_percent / 100)
    dim = (width, height)


    resized = cv2.resize(result, dim, interpolation = cv2.INTER_AREA)

    cv2.imshow("edges", resized)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    """
    #tmp_image_name= image_name + "-kmeans-" + str(K) + str(random.random()) + ".png"
    #cv2.imwrite(path_to_save_segments+tmp_image_name, result)
    return result

请原谅注释掉的代码,那是我在修改算法时观察我对图像所做的更改。

我相信您正在尝试做的是所谓的“实例分割”。这个过程最好用深度学习技术来完成,除非你能找到一个预训练的模型,否则它可能不适合你。这是一篇关于如何做到这一点的文章:https://www.analyticsvidhya.com/blog/2019/04/introduction-image-segmentation-techniques-python/

一个更简单(但不太准确)的解决方案可能是使用 RGB 阈值来创建图片的“轮廓”,然后使用填充算法来辨别结果图像中的特定像素。要绘制图像的粗略轮廓,您必须尝试使用​​不同的阈值。首先,将整个图像转换为灰度w。 OpenCV(cv2.imread('image-name', 0))。要测试不同的阈值,只需检查每个像素值是否高于或低于某个值(您选择):

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

def apply_threshold(img_array, threshold):
    threshold_applied = []
    for row in img_array:
        threshold_applied.append([])
        for pixel in row:
            if pixel>threshold:
                threshold_applied[len(threshold_applied)-1].append([255, 255, 255])
            else:
                threshold_applied[len(threshold_applied)-1].append([0, 0, 0])
    new_img = np.array(threshold_applied, np.uint8)
    cv2.imwrite(str(threshold)+".jpg", new_img)

img = cv2.imread("Ek1Dx.jpg", 0)
apply_threshold(img, 210)

如果您 运行 代码,您会发现输出非常不准确,几乎看不到玉米棒子和玉米粒。在过去十年左右的时间里,实例分割和分割图像实际上一直是一个非常大的计算机科学问题。您可能可以使用其他过滤器来获得更准确的结果,但我认为识别阈值是一种基线图像分割技术。如果你愿意,你可以尝试调整程序中的阈值,看看它是否会给你一个更分裂的图像。