新图片:test image
我正在尝试量化微血管视频中两个轮廓之间的距离(请参见快照)
图像分析结构
现在,我只能选择一个轮廓(已勾勒轮廓),并且正在从该轮廓中获取尺寸,但是我要选择的是结构和度量的顶部和底部轮廓距离(在快照中标有橙色线和A)。
有什么建议吗?我的视频分析代码如下。感谢您的提前帮助!:
import cv2
import pandas as pd
import numpy as np
import imutils
from scipy.spatial import distance as dist
from imutils import perspective
from imutils import contours
videocapture = cv2.VideoCapture('RTMLV.mp4')
def safe_div(x,y):
if y==0: return 0
return x/y
def nothing(x):
pass
def rescale_frame(frame, percent=100): #make the video windows a bit smaller
width = int(frame.shape[1]*percent/100)
height = int(frame.shape[0]*percent/100)
dim = (width, height)
return cv2.resize(frame, dim, interpolation=cv2.INTER_AREA)
if not videocapture.isOpened():
print("Unable to open video")
exit()
windowName="Vessel Tracking"
cv2.namedWindow(windowName)
# Sliders to adjust image
cv2.createTrackbar("Threshold", windowName, 75, 255, nothing)
cv2.createTrackbar("Kernel", windowName, 5, 30, nothing)
cv2.createTrackbar("Iterations", windowName, 1, 10, nothing)
showLive=True
while(showLive):
ret, frame=videocapture.read()
frame_resize=rescale_frame(frame)
if not ret:
print("Cannot capture the frame")
exit()
thresh = cv2.getTrackbarPos("Threshold", windowName)
ret,thresh1 = cv2.threshold(frame_resize, thresh, 255, cv2.THRESH_BINARY)
kern = cv2.getTrackbarPos("Kernel", windowName)
kernel = np.ones((kern, kern), np.uint8) # square image kernel used for erosion
itera=cv2.getTrackbarPos("Iterations", windowName)
dilation = cv2.dilate(thresh1, kernel, iterations=itera)
erosion = cv2.erode(dilation, kernel, iterations=itera) #refines all edges in the binary image
opening = cv2.morphologyEx(erosion, cv2.MORPH_OPEN, kernel)
closing = cv2.morphologyEx(opening, cv2.MORPH_CLOSE, kernel)
closing = cv2.cvtColor(closing, cv2.COLOR_BGR2GRAY)
contours,hierarchy = cv2.findContours(closing,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE) # find contours with simple approximation cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE
closing = cv2.cvtColor(closing,cv2.COLOR_GRAY2RGB)
cv2.drawContours(closing, contours, -1, (128,255,0), 1)
# focus on only the largest outline by area
areas = [] #list to hold all areas
for contour in contours:
ar = cv2.contourArea(contour)
areas.append(ar)
max_area = max(areas)
max_area_index = areas.index(max_area) # index of the list element with largest area
cnt = contours[max_area_index - 1] # largest area contour is usually the viewing window itself, why?
cv2.drawContours(closing, [cnt], 0, (0,0,255), 1)
def midpoint(ptA, ptB):
return ((ptA[0] + ptB[0]) * 0.5, (ptA[1] + ptB[1]) * 0.5)
# compute the rotated bounding box of the contour
orig = frame_resize.copy()
box = cv2.minAreaRect(cnt)
box = cv2.cv.BoxPoints(box) if imutils.is_cv2() else cv2.boxPoints(box)
box = np.array(box, dtype="int")
# order the points in the contour such that they appear
# in top-left, top-right, bottom-right, and bottom-left
# order, then draw the outline of the rotated bounding
# box
box = perspective.order_points(box)
cv2.drawContours(orig, [box.astype("int")], -1, (0, 255, 0), 1)
# loop over the original points and draw them
for (x, y) in box:
cv2.circle(orig, (int(x), int(y)), 5, (0, 0, 255), -1)
# unpack the ordered bounding box, then compute the midpoint
# between the top-left and top-right coordinates, followed by
# the midpoint between bottom-left and bottom-right coordinates
(tl, tr, br, bl) = box
(tltrX, tltrY) = midpoint(tl, tr)
(blbrX, blbrY) = midpoint(bl, br)
# compute the midpoint between the top-left and top-right points,
# followed by the midpoint between the top-right and bottom-right
(tlblX, tlblY) = midpoint(tl, bl)
(trbrX, trbrY) = midpoint(tr, br)
# draw the midpoints on the image
cv2.circle(orig, (int(tltrX), int(tltrY)), 5, (255, 0, 0), -1)
cv2.circle(orig, (int(blbrX), int(blbrY)), 5, (255, 0, 0), -1)
cv2.circle(orig, (int(tlblX), int(tlblY)), 5, (255, 0, 0), -1)
cv2.circle(orig, (int(trbrX), int(trbrY)), 5, (255, 0, 0), -1)
# draw lines between the midpoints
cv2.line(orig, (int(tltrX), int(tltrY)), (int(blbrX), int(blbrY)),(255, 0, 255), 1)
cv2.line(orig, (int(tlblX), int(tlblY)), (int(trbrX), int(trbrY)),(255, 0, 255), 1)
cv2.drawContours(orig, [cnt], 0, (0,0,255), 1)
# compute the Euclidean distance between the midpoints
dA = dist.euclidean((tltrX, tltrY), (blbrX, blbrY))
dB = dist.euclidean((tlblX, tlblY), (trbrX, trbrY))
# compute the size of the object
P2M4x = 1.2
P2M10x = 3.2
P2M20x = 6
pixelsPerMetric = P2M10x # Pixel to micron conversion
dimA = dA / pixelsPerMetric
dimB = dB / pixelsPerMetric
dimensions = [dimA, dimB]
# draw the object sizes on the image
cv2.putText(orig, "{:.1f}um".format(dimA), (int(tltrX - 15), int(tltrY - 10)), cv2.FONT_HERSHEY_SIMPLEX, 0.65, (255, 255, 255), 2)
cv2.putText(orig, "{:.1f}um".format(dimB), (int(trbrX + 10), int(trbrY)), cv2.FONT_HERSHEY_SIMPLEX, 0.65, (255, 255, 255), 2)
# compute the center of the contour
M = cv2.moments(cnt)
cX = int(safe_div(M["m10"],M["m00"]))
cY = int(safe_div(M["m01"],M["m00"]))
# draw the contour and center of the shape on the image
cv2.circle(orig, (cX, cY), 5, (255, 255, 255), -1)
cv2.putText(orig, "center", (cX - 20, cY - 20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 2)
cv2.imshow(windowName, orig)
cv2.imshow('', closing)
if cv2.waitKey(30)>=0:
showLive=False
videocapture.release()
cv2.destroyAllWindows()
答案 0 :(得分:0)
已对该答案进行了编辑,以响应添加到帖子中的新测试图像。
我无法使用您上载的代码在测试图像中分割血管。我使用手动注释和GrabCut算法对图像进行了分割。
这是我用于手动细分的代码:
import cv2, os, numpy as np
import time
# Plot with Matplotlib
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
img_path = '/home/stephen/Desktop/0lszR.jpg'
img = cv2.imread(img_path)
img = img[420:1200, :]
h,w,_ = img.shape
mask = np.zeros((h,w), np.uint8)
mask[:] = 2
src = img.copy()
h,w,_ = img.shape
drawing = src.copy()
# Mouse callback function
global k, px, py
k = 0
px, py = 0,0
def callback(event, x, y, flags, param):
global k, px, py
print(x,y, k, px, py)
if k == 115: # 's' for sure background
if px+py!=0:
cv2.line(img, (x,y), (px, py), (255,255,0), 8)
cv2.line(mask, (x,y), (px, py), 0, 8)
if k == 116: # 't' for sure foreground
if px+py!=0:
cv2.line(img, (x,y), (px, py), (0,255,255), 8)
cv2.line(mask, (x,y), (px, py), 1, 8)
else: print(px, py)
px, py = x,y
#if k != 115 or 116: px, py = 0,0
cv2.namedWindow('img')
cv2.setMouseCallback('img', callback)
while k != 27:
cv2.imshow('img', img)
k_temp = cv2.waitKey(1)
if k_temp!=-1: k = k_temp
cv2.destroyAllWindows()
找到分割的图像后,我使用函数np.nonzero()
查找列的顶部和底部:
这是我用来查找宽度的代码:
# Initialize parameters for the GrabCut algorithm
bgdModel = np.zeros((1,65),np.float64)
fgdModel = np.zeros((1,65),np.float64)
# Apply GrabCut
out_mask = mask.copy()
out_mask, _, _ = cv2.grabCut(src,out_mask,None,bgdModel,fgdModel,1,cv2.GC_INIT_WITH_MASK)
out_mask = np.where((out_mask==2)|(out_mask==0),0,1).astype('uint8')
# Open the mask to fill in the holes
out_img = src*out_mask[:,:,np.newaxis]
flip_mask = cv2.flip(out_mask, 0)
# Find the distances
distances = []
for col_num in range(src.shape[1]-1):
col = out_mask[:, col_num:col_num+1]
flip_col = flip_mask[:, col_num:col_num+1]
top = np.nonzero(col)[0][0]
bottom = h-np.nonzero(flip_col)[0][0]
if col_num % 12 == 0:
cv2.line(drawing, (col_num, top), (col_num, bottom), (234,345,34), 4)
distances.append(bottom-top)
f, axarr = plt.subplots(2,3, sharex=True)
axarr[0,0].imshow(src)
axarr[0,1].imshow(out_mask)
axarr[0,2].imshow(drawing)
axarr[1,0].imshow(img)
axarr[1,1].imshow(out_img)
axarr[1,2].plot(distances)
axarr[0,0].set_title("Source")
axarr[0,1].set_title('Mask from GrabCut')
axarr[0,2].set_title('Widths')
axarr[1,0].set_title('Manual Annotation')
axarr[1,1].set_title('GrabCut Mask')
axarr[1,2].set_title('Graph of Width')
axarr[0,0].axis('off')
axarr[0,1].axis('off')
axarr[1,0].axis('off')
axarr[1,1].axis('off')
axarr[1,2].axis('off')
axarr[0,2].axis('off')
plt.show()