Ищем разные методы сегментации изображения для картинок яблок - PullRequest
6 голосов
/ 11 февраля 2020

У меня есть фотографии кусочков яблок, пропитанных раствором йода. Цель состоит в том, чтобы разделить яблоки на отдельные области интереса и оценить уровень крахмала каждого. Это для школьного проекта, поэтому моя цель - протестировать различные методы сегментации и объективно найти лучшее решение, будь то один метод или комбинация нескольких методов.

Проблема в том, что до сих пор у меня есть только Подойди ближе к одному методу. Этот метод использует HoughCircles. Первоначально я планировал использовать метод Watershed, морфологические операции или простое определение порога. Этот план потерпел неудачу, когда я не смог изменить ни одну из них для работы.

Исходные изображения выглядят примерно так, с различными оттенками темноты яблока

The original images look similar to this, with varying shades of darkness of the apple

Я пытался удалить фоновый трей, используя cv2.inRange со значениями HSV, но он не работает с более темными яблоками.

Это то, что HoughCircles произвела на исходном изображении с применением оттенков серого и медианного размытия, а также с помощью попытки маски на лотке.

This is what the HoughCircles produced on the original image with a grayscale and median blur applied, also with an attempted mask of the tray.

Любой совет или направление о том, где искать дальше будет принята с благодарностью. Я могу предоставить код, который я использую, если это поможет.

Спасибо!

РЕДАКТИРОВАТЬ 1: Добавление некоторого кода и уточнение вопроса

Спасибо за ответы , Мой реальный вопрос: есть ли другие методы сегментации, которым хорошо подходит этот сценарий? Я хотел бы попробовать несколько разных методов и сравнить результаты на большом наборе фотографий. Моя следующая попытка - использование сегментации k-средних. Также я добавлю код ниже, чтобы показать, что я пробовал до сих пор.

HSV COLOR FILTERING

import cv2
import numpy as np

# Load image
image = cv2.imread('ApplePic.jpg')

# Set minimum and max HSV values to display
lower = np.array([0, 0, 0])
upper = np.array([105, 200, 255])

# Create HSV Image and threshold into a range.
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv, lower, upper)
maskedImage = cv2.bitwise_and(image, image, mask=mask)

# Show Image
cv2.imshow('HSV Mask', image)
cv2.waitKey(0)

HoughCircles

# import the necessary packages
import numpy as np
import argparse
import cv2
import os

directory = os.fsencode('Photos\\Sample N 100')

for file in os.listdir(directory):

    filename = os.fsdecode(file)

    if filename.endswith('.jpg'):
        # Load the image
        image = cv2.imread('Photos\\Sample N 100\\' + filename)

        # Calculate scale
        scale_factor = 800 / image.shape[0]
        width = int(image.shape[1] * scale_factor)
        height = 800
        dimension = (width, height)
        min_radius = int((width / 10) * .8)
        max_radius = int((width / 10) * 1.2)

        # Resize image
        image = cv2.resize(image, dimension, interpolation=cv2.INTER_AREA)

        # Copy Image 
        output = image.copy()

        # Grayscale Image
        gray = cv2.medianBlur(cv2.cvtColor(image, cv2.COLOR_BGR2GRAY), 5)

        # Detect circles in image
        circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1, min_radius * 2, 4, 60, 20,  min_radius, max_radius)

        # 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[0, :]).astype("int")
            # loop over the (x, y) coordinates and radius of the circles
            for (x, y, r) in circles:
                # 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)
                cv2.putText(output, '(' + str(x) + ',' + str(y) + ',' + str(r) + ')', (x, y),
                        cv2.FONT_HERSHEY_COMPLEX_SMALL, 1, 255)


            # show the output image
            cv2.imshow("output", np.hstack([image, output, maskedImage]))
            cv2.waitKey(0)
        continue
    else:
        continue

1 Ответ

6 голосов
/ 11 февраля 2020

Альтернативный подход к сегментированию яблок состоит в том, чтобы выполнить сегментацию цвета Kmeans перед установлением порога, а затем использовать контурную фильтрацию для выделения объектов яблока:

  1. Применение цветовой сегментации Kmeans. Мы загружаем изображение, уменьшаем его, используя imutils.resize, затем применяем цветовую сегментацию Kmeans. В зависимости от количества кластеров мы можем сегментировать изображение на желаемое количество цветов.

  2. Получение двоичного изображения. Далее мы конвертируем в градации серого, размытие по Гауссу и порог Оцу.

  3. Фильтр используя аппроксимацию контуров. Мы отфильтровываем некруглые контуры и небольшой шум.

  4. Морфологические операции. Мы выполняем морфинг, близкий к заполнению соседних контуров

  5. Нарисуйте минимальные окружающие круги, используя область контура в качестве фильтра. Мы находим контуры и рисуем приближенные круги. Для этого мы используем два участка, один, где был хороший порог, и другой, где мы приближаем радиус.


Квантизация цвета Kmeans с clusters=3 и двоичным изображением

image image

Закрытие морфа и результат

image image

"Хорошие" контуры, радиус которых был автоматически рассчитан с помощью cv2.minEnclosingCircle, выделяются зеленым, а аппроксимированные контуры выделяются чирком. Эти аппроксимированные контуры были плохо сегментированы от процесса пороговой обработки, поэтому мы усредняем «хороший» радиус контуров и используем его для рисования круга.

Код

import cv2
import numpy as np
import imutils

# Kmeans color segmentation
def kmeans_color_quantization(image, clusters=8, rounds=1):
    h, w = image.shape[:2]
    samples = np.zeros([h*w,3], dtype=np.float32)
    count = 0

    for x in range(h):
        for y in range(w):
            samples[count] = image[x][y]
            count += 1

    compactness, labels, centers = cv2.kmeans(samples,
            clusters, 
            None,
            (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10000, 0.0001), 
            rounds, 
            cv2.KMEANS_RANDOM_CENTERS)

    centers = np.uint8(centers)
    res = centers[labels.flatten()]
    return res.reshape((image.shape))

# Load image, resize smaller, perform kmeans, grayscale
# Apply Gaussian blur, Otsu's threshold
image = cv2.imread('1.jpg')
image = imutils.resize(image, width=600)
kmeans = kmeans_color_quantization(image, clusters=3)
gray = cv2.cvtColor(kmeans, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (9,9), 0)
thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]

# Filter out contours not circle
cnts = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    peri = cv2.arcLength(c, True)
    approx = cv2.approxPolyDP(c, 0.04 * peri, True)
    if len(approx) < 4:
        cv2.drawContours(thresh, [c], -1, 0, -1)

# Morph close
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5,5))
close = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel, iterations=3)

# Find contours and draw minimum enclosing circles 
# using contour area as filter
approximated_radius = 63
cnts = cv2.findContours(close, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    area = cv2.contourArea(c)
    x,y,w,h = cv2.boundingRect(c)
    # Large circles
    if area > 6000 and area < 15000:
        ((x, y), r) = cv2.minEnclosingCircle(c)
        cv2.circle(image, (int(x), int(y)), int(r), (36, 255, 12), 2)
    # Small circles
    elif area > 1000 and area < 6000:
        ((x, y), r) = cv2.minEnclosingCircle(c)
        cv2.circle(image, (int(x), int(y)), approximated_radius, (200, 255, 12), 2)

cv2.imshow('kmeans', kmeans)
cv2.imshow('thresh', thresh)
cv2.imshow('close', close)
cv2.imshow('image', image)
cv2.waitKey()     
...