如何在图像中找到区域maxima/minima?

How to find regional maxima/minima in images?

我正在尝试找出这张图片中的区域最大值:

像这样在其位置上进行切割:

我找到了一种过滤区域最大值的方法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()

两者都导致:

看到一种不限于水平像素的方法会很有趣。

您可以尝试使用 morphological opening 操作在过滤区域最大值后将两部分分开。根据您所在区域的最小厚度,使用更大的内核或多次开盘调用。

要使用 opening 找到确切的切割位置,您可以进行多次后续打开操作调用,直到单个 blob 一分为二。您可以检测到您使用 cv::detectContours().

获得的位置分析轮廓

您可能还会发现 distanceTransform() 有用。其结果是每个点到最近边界的距离。思路是做图像骨架化,沿着骨架线取distanceTransform()个结果的最小值,找到切割位置。

您也可以尝试基于白色像素位置的 k = 2 的 k 均值聚类。切割线将在簇之间。

编辑: 你可能会发现这个 page 很有用,因为人们在那里讨论类似的问题。其中一个答案使用 cv::convexityDefects() 来查找分离点。

如果您的 "chromatids"(我将以这种方式引用显示的结构,因为它看起来有点像一个)都以这种方式对齐,您可以简单地计算每行的白色像素,并且搜索最小值。

请看下面的代码,希望它是不言自明的:

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)

输出如下所示:

如果您的 "chromatids" 方向不同,可以根据染色单体的 "principal component" 在上述代码之前使用一些旋转。

希望对您有所帮助!