Reputation: 5841
I am trying to find the regional maximum in this image:
to make a cut in its position like this:
I found a method how to filter regional maxima here but I can't make it work for my case.
My code so far:
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()
My Solution:
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 wrote a more professional approach:
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()
Both result in:
It would be interesting to see an approach, that is not limited to horizontal pixels.
Upvotes: 1
Views: 2013
Reputation: 18925
If your "chromatids" (I will refer to the shown structure in that way, because it kind of looks like one) are all aligned in that way, you can simply count the white pixels per row, and search for the minimum.
Please have a look at the following code, which is hopefully self-explanatory:
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)
The outputs look like this:
If your "chromatids" have varying orientations, one could utilize some rotation prior to the above-mentioned code, based on the "principal component" of the chromatid.
Hope that helps!
Upvotes: 3
Reputation: 1647
You can try to use morphological opening operation for separating that two pieces after filtering regional maxima. Use larger kernel or multiple opening calls depending on your regional minima thickness.
To find the exact cut position with the opening you may do several subsequent opening operation calls until the single blob splits into two. You can detect that position analyzing contours you get with cv::detectContours()
.
Also you may find distanceTransform() usefull. Its result is the distance to the closest boundary from each point. The idea is to do image skeletonization and take the minima of distanceTransform()
results along the skeleton line to find the cut position.
Also you can try k-means clusterization with k = 2 based on white pixel positions. The cut line will be between clusters.
Edit:
you may find useful this page, as guys discuss similar problem there. One of the answers uses cv::convexityDefects()
to find the separation points.
Upvotes: 1