Как выбрать все черные пиксели, которые соприкасаются с краем изображения в PIL? - PullRequest
3 голосов
/ 09 апреля 2019

У меня есть набор изображений чашек Петри, которые, к сожалению, не самого высокого качества (пример ниже, оси не являются частью изображений). dish1 Я пытаюсь выбрать фон и рассчитать его площадь в пикселях следующим образом:

image = Image.open(path)
black_image = 1 * (np.asarray(image.convert('L')) < 12)
black_region = black_image.sum()

Это дает следующее:

enter image description here

Если я более строг с выбором черных пикселей, я пропускаю пиксели на других изображениях, и если я становлюсь более свободным, я в конечном итоге выбираю слишком много самой чашки Петри. Есть ли способ, которым я могу выбрать только те пиксели, которые имеют значение яркости меньше 12 И являются смежными с краем? Я тоже открыт для решений openCV.

Ответы [ 4 ]

3 голосов
/ 10 апреля 2019

Надеюсь, я не упрощаю проблему, но, с моей точки зрения, использование OpenCV с простыми порогами, морфологические операции и findContours должны сделать эту работу.

Пожалуйста, смотрите следующий код:

import cv2
import numpy as np

# Input
input = cv2.imread('images/x0ziO.png', cv2.IMREAD_COLOR)

# Input to grayscale
gray = cv2.cvtColor(input, cv2.COLOR_BGR2GRAY)

# Binary threshold
_, gray = cv2.threshold(gray, 20, 255, cv2.THRESH_BINARY)

# Morphological improvements of the mask
gray = cv2.morphologyEx(gray, cv2.MORPH_OPEN, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)))
gray = cv2.morphologyEx(gray, cv2.MORPH_CLOSE, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (11, 11)))

# Find contours
cnts, _ = cv2.findContours(gray, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)

# Filter large size contours; at the end, there should only be one left
largeCnts = []
for cnt in cnts:
    if (cv2.contourArea(cnt) > 10000):
        largeCnts.append(cnt)

# Draw (filled) contour(s)
gray = np.uint8(np.zeros(gray.shape))
gray = cv2.drawContours(gray, largeCnts, -1, 255, cv2.FILLED)

# Calculate background pixel area
bgArea = input.shape[0] * input.shape[1] - cv2.countNonZero(gray)

# Put result on input image
input = cv2.putText(input, 'Background area: ' + str(bgArea), (20, 30), cv2.FONT_HERSHEY_COMPLEX_SMALL, 1.0, (255, 255, 255))

cv2.imwrite('images/output.png', input)

Промежуточное изображение «маски» выглядит так:

Mask

И конечный результат выглядит следующим образом:

Output

3 голосов
/ 09 апреля 2019

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

enter image description here

Теперь посмотрите, где линии изменяются с черного на белый, а затем с белого на черный (сверху обведены красным). К сожалению, ваши изображения имеют аннотации и оси, которые мне пришлось обрезать, поэтому ваш номер не будет одинаковым. В верхней строке / строке мое изображение меняется с черного на белое в столбце 319 и обратно на черный в столбце 648. Если я сложу их вместе, я получу 966 и разделю на 2, центр изображения на оси x будет в столбце 483 .

Глядя на нижнюю строку / строку, переходы (обведены красным) находятся в столбцах 234 и 736, которые в сумме составляют 970, что составляет 485 при усреднении, поэтому мы знаем, что центр круга находится в вертикальном столбце изображения 483-485 или, скажем, 484.

Тогда вы сможете определить центр и радиус изображения и замаскировать изображение для точного расчета фона.

2 голосов
/ 09 апреля 2019

Поскольку вы открыты для подходов OpenCV, вы можете использовать SimpleBlobDetector

Очевидно, что полученный результат тоже не идеален, так как нужно установить много гиперпараметров. Гиперпараметры делают его довольно гибким, поэтому с него можно начинать достойно.

Это то, что делает детектор (см. Подробности здесь ):

  1. Определение порога : преобразование исходных изображений в несколько двоичных изображений путем установки порогового значения исходного изображения с пороговыми значениями, начинающимися с minThreshold. Эти пороговые значения увеличиваются на thresholdStep до maxThreshold. Таким образом, первый порог равен minThreshold, второй - minThreshold + thresholdStep, третий - minThreshold + 2 x thresholdStep и т. Д.
  2. Группировка : В каждом двоичном изображении связанные белые пиксели сгруппированы вместе. Давайте назовем эти двоичные двоичные объекты.
  3. Объединение : центры двоичных объектов в двоичных изображениях вычисляются, а объекты, расположенные ближе, чем minDistBetweenBlobs, объединяются.

  4. Расчет центра и радиуса : Центры и радиусы новых слитых капель вычисляются и возвращаются.

Найдите код под изображением.

Output Image

# Standard imports
import cv2
import numpy as np

# Read image
im = cv2.imread("petri.png", cv2.IMREAD_COLOR)

# Setup SimpleBlobDetector parameters.
params = cv2.SimpleBlobDetector_Params()

# Change thresholds
params.minThreshold = 0
params.maxThreshold = 255

# Set edge gradient
params.thresholdStep = 5

# Filter by Area.
params.filterByArea = True
params.minArea = 10

# Set up the detector with default parameters.
detector = cv2.SimpleBlobDetector_create(params)

# Detect blobs.
keypoints = detector.detect(im)

# Draw detected blobs as red circles.
# cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS ensures the size of the circle corresponds to the size of blob
im_with_keypoints = cv2.drawKeypoints(im, keypoints, np.array([]), (0, 0, 255),
                                      cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)

# Show keypoints
cv2.imshow("Keypoints", im_with_keypoints)
cv2.waitKey(0)
2 голосов
/ 09 апреля 2019

Попробуйте экспериментальный метод floodfill().https://pillow.readthedocs.io/en/5.1.x/reference/ImageDraw.html?highlight=floodfill#PIL.ImageDraw.PIL.ImageDraw.floodfill

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

См. Также Сегментация изображения с помощью алгоритма Watershed , который очень похож на заливку, но не полагаясь на один уникальный цвет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...