Как найти региональные максимумы / минимумы в изображениях? - PullRequest
1 голос
/ 02 мая 2019

Я пытаюсь найти региональный максимум на этом изображении:

enter image description here

, чтобы сделать разрез в своей позиции следующим образом:

enter image description here

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

Мой код:

import numpy as np
import cv2
import skimage as sm
from skimage.morphology import reconstruction
import scipy as sp

img = cv2.imread('img.png', 0)

img = sm.img_as_float(img)
img = sp.ndimage.gaussian_filter(img, 1)

seed = np.copy(img)
seed[1:-1,1:-1] = img.min()
mask = img
dilated = reconstruction(seed, mask, method = 'dilation')
img = img - dilated

cv2.imshow('img', img)
cv2.waitKey()

Мое решение:

import numpy as np
import cv2

img = cv2.imread('img.png', 0)

_, thresh = cv2.threshold(img, 250, 255, cv2.THRESH_BINARY)

rows = np.sum(thresh/255, axis = 1)
ol = len(np.nonzero(rows)[0])
L = []
z = 0
for idx, row in enumerate(rows):
    if row > 0:
        if z > 5 and z < ol - 5:
            L.append(idx)
        z += 1
split = np.min(rows[L])
thresh[np.where(rows == split)[0][0]] = 0

cv2.imshow('img', thresh)
cv2.waitKey()

HansHirse написал более профессиональный подход:

import numpy as np
import cv2

img = cv2.imread('img.png', 0)

_, thresh = cv2.threshold(img, 250, 255, cv2.THRESH_BINARY)

rows = np.sum(thresh/255, axis = 1)
exclude = 5
idx = np.where(rows > 0)[0]
idx = idx[exclude : len(idx) - exclude]
cut = idx[np.argmin(rows[idx])]
thresh[cut] = 0

cv2.imshow('img', thresh)
cv2.waitKey()

Оба результата дают:

enter image description here

Было бы интересно увидеть подход, который не ограничивается горизонтальными пикселями.

Ответы [ 2 ]

3 голосов
/ 02 мая 2019

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

Пожалуйста, взгляните на следующий код, который, надеюсь, не требует пояснений:

import cv2
import numpy as np

# Load input image
input = cv2.imread('images/Q6YM9.png', cv2.IMREAD_GRAYSCALE)

# Extract "chromatid" (the structure looks like one...)
_, chromatid = cv2.threshold(input, 250, 255, cv2.THRESH_BINARY)

# Sum row-wise pixel values
rowPixelSum = np.sum(chromatid / 255, axis=1)

# Detect all rows with non-zero elements
ind = np.where(rowPixelSum > 0)[0]

# Exclude n rows at the top and bottom of the "chromatid"
# Caveat: Check for plausibility (index out of bounds, etc.)
nEx = 15
ind = ind[15:len(ind)-nEx]

# Detect index of row with minimum pixel count
cutRow = ind[np.argmin(rowPixelSum[ind])]

# Detect start and end of "chromatid" on row with minimum pixel count
row = np.where(chromatid[cutRow, :] > 0)[0]
xStart = row[0]
xEnd = row[-1]

# For visualization: Draw black line through row with minimum pixel count
cv2.line(input, (xStart, cutRow), (xEnd, cutRow), 0, 3)
cv2.line(chromatid, (xStart, cutRow), (xEnd, cutRow), 0, 3)

# Write output image
cv2.imwrite('images\input.png', input)
cv2.imwrite('images\chromatid.png', chromatid)

Выводы выглядят так:

Chromatid

Input

Если ваши «хроматиды» имеют различные ориентации, можно использовать некоторое вращение до вышеупомянутого кода, основываясь на «главном компоненте» хроматиды.

Надеюсь, это поможет!

1 голос
/ 02 мая 2019

Вы можете попытаться использовать морфологическое открытие операцию для разделения этих двух частей после фильтрации региональных максимумов. Используйте ядро ​​большего размера или несколько начальных вызовов в зависимости от толщины минимума вашего региона.

Чтобы найти точную позицию обрезки при открытии , вы можете сделать несколько последующих вызовов операции открытия, пока один большой двоичный объект не разделится на два. Вы можете определить эту позицию, анализируя полученные контуры с помощью cv::detectContours().

Также вы можете найти distanceTransform () полезной. Его результатом является расстояние до ближайшей границы от каждой точки. Идея состоит в том, чтобы выполнить скелетонизацию изображения и взять минимумы результатов distanceTransform() вдоль линии скелета, чтобы найти положение обрезки.

Также вы можете попробовать кластеризацию k-средних с k = 2 на основе положения белого пикселя. Линия разреза будет между кластерами.

Edit: Вам может пригодиться эта страница , так как там обсуждают схожие проблемы. Один из ответов использует cv::convexityDefects(), чтобы найти точки разделения.

...