【问题标题】:Pixels intensity values between two lines两条线之间的像素强度值
【发布时间】:2020-03-17 05:29:49
【问题描述】:

我创建了一个算法,可以检测挤出的胶原蛋白肠衣的边缘,并在图像上的这些边缘之间绘制一条中心线。 Casing with a centerline.

这是我的代码:

import numpy as np
import matplotlib.pyplot as plt
plt.style.use('fivethirtyeight')

img = cv2.imread("C:/Users/5.jpg", cv2.IMREAD_GRAYSCALE)
img = cv2.resize(img, (1500, 1200))

#ROI
fromCenter = False
r = cv2.selectROI(img, fromCenter)
imCrop = img[int(r[1]):int(r[1]+r[3]), int(r[0]):int(r[0]+r[2])]

#Operations on an image
_,thresh = cv2.threshold(imCrop,100,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)

kernel = np.ones((5,5),np.uint8)
opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)

blur = cv2.GaussianBlur(opening,(7,7),0)

edges = cv2.Canny(blur, 0,20)

#Edges localization, packing coords into a list
indices = np.where(edges != [0])
coordinates = list(zip(indices[1], indices[0]))
num = len(coordinates)

#Separating into top and bot edge
bot_cor = coordinates[:int(num/2)]
top_cor = coordinates[-int(num/2):]

#Converting to arrays, sorting
a, b = np.array(top_cor), np.array(bot_cor)
a, b = a[a[:,0].argsort()], b[b[:,0].argsort()]

#Edges approximation by a 5th degree polynomial
min_a_x, max_a_x = np.min(a[:,0]), np.max(a[:,0])
new_a_x = np.linspace(min_a_x, max_a_x, imCrop.shape[1])
a_coefs = np.polyfit(a[:,0],a[:,1], 5)
new_a_y = np.polyval(a_coefs, new_a_x)

min_b_x, max_b_x = np.min(b[:,0]), np.max(b[:,0])
new_b_x = np.linspace(min_b_x, max_b_x, imCrop.shape[1])
b_coefs = np.polyfit(b[:,0],b[:,1], 5)
new_b_y = np.polyval(b_coefs, new_b_x)

#Defining a centerline
midx = [np.average([new_a_x[i], new_b_x[i]], axis = 0) for i in range(imCrop.shape[1])]
midy = [np.average([new_a_y[i], new_b_y[i]], axis = 0) for i in range(imCrop.shape[1])]

plt.figure(figsize=(16,8))
plt.title('Cross section')
plt.xlabel('Length of the casing', fontsize=18)
plt.ylabel('Width of the casing', fontsize=18)
plt.plot(new_a_x, new_a_y,c='black')
plt.plot(new_b_x, new_b_y,c='black')
plt.plot(midx, midy, '-', c='blue')
plt.show()

#Converting coords type to a list (plotting purposes)
coords = list(zip(midx, midy))
points = list(np.int_(coords))

mask = np.zeros((imCrop.shape[:2]), np.uint8)
mask = edges

#Plotting
for point in points:
    cv2.circle(mask, tuple(point), 1, (255,255,255), -1)

for point in points:
    cv2.circle(imCrop, tuple(point), 1, (255,255,255), -1)  

cv2.imshow('imCrop', imCrop)
cv2.imshow('mask', mask)

cv2.waitKey(0)
cv2.destroyAllWindows()

现在我想总结顶部边缘和中心线之间区域中每个像素的强度(中心线和底部边缘之间的区域也是如此)。

有没有办法将 ROI 限制在检测到的边缘之间的区域,并根据计算出的中心线将其分成两个区域?

或者有什么方法可以根据它们的坐标访问边缘和中心线之间包含的像素?

(这是我在这里的第一篇文章,对于所有错误提前道歉)

【问题讨论】:

  • 如果所有图像都与您的示例图像非常相似,您可能可以分离背景并创建两个蒙版(用于上部和下部)并使用它们来隔离您的 ROI。你有没有尝试过这样的事情?

标签: python opencv


【解决方案1】:

我写了一个有点幼稚的代码来获取上下部分的掩码。我的代码认为源图像总是和你的一样:带有水平条纹。

申请Canny后我得到了这个:

然后我通过图像数组运行一些循环来填充图像中不需要的区域。这是分别为上部和下部完成的,创建蒙版。结果是:

然后您可以使用此掩码仅对您感兴趣的元素求和,使用cv.sumElems

import cv2 as cv

#open as grayscale image
src = cv.imread("colagen.png",cv.IMREAD_GRAYSCALE)

# apply canny and find contours
threshold = 100
canny_output = cv.Canny(src, threshold, threshold * 2)

# find mask for upper part
mask1 = canny_output.copy()
x, y = canny_output.shape
area = 0
for j in range(y):
    area = 0
    for i in range(x):
        if area == 0:
            if mask1[i][j] > 0:
                area = 1
                continue
            else: 
                mask1[i][j] = 255
        elif area == 1:
            if mask1[i][j] > 0:
                area = 2
            else: 
                continue
        else:
            mask1[i][j] = 255
mask1 = cv.bitwise_not(mask1)

# find mask for lower part         
mask2 = canny_output.copy()
x, y = canny_output.shape
area = 0
for j in range(y):
    area = 0
    for i in range(x):
        if area == 0:
            if mask2[-i][j] > 0:
                area = 1
                continue
            else: 
                mask2[-i][j] = 255
        elif area == 1:
            if mask2[-i][j] > 0:
                area = 2
            else: 
                continue
        else:
            mask2[-i][j] = 255
mask2 = cv.bitwise_not(mask2)

# apply masks and calculate sum of elements in upper and lower part
sums = [0,0]
(sums[0],_,_,_) = cv.sumElems(cv.bitwise_and(src,mask1))
(sums[1],_,_,_) = cv.sumElems(cv.bitwise_and(src,mask2))

cv.imshow('src',src)
cv.imshow('canny',canny_output)
cv.imshow('mask1',mask1)
cv.imshow('mask2',mask2)
cv.imshow('masked1',cv.bitwise_and(src,mask1))
cv.imshow('masked2',cv.bitwise_and(src,mask2))
cv.waitKey()

替代方案...

可能存在一些填充 Canny 结果区域的函数。我尝试了cv.fillPolycv.floodFill,但没有设法让它们轻松工作......但也许其他人可以帮助你......

编辑

找到了另一种使用更简洁代码获取掩码的方法。使用 numpy np.add.accumulate 然后 np.clip,然后是模运算:

# first divide canny_output by 255 to get 0's and 1's, then perform
# an accumulate addition for each column. Thus you'll get +1 for every
# line, "painting" areas with 1, 2, 3...
a = np.add.accumulate(canny_output/255,0)
# clip values: anything greater than 2 becomes 2
a = np.clip(a, 0, 2)
# performe a modulo, to get areas alternating with 0 or 1; then multiply by 255
a = a%2 * 255
# convert to uint8
mask1 = cv.convertScaleAbs(a)

# to get mask2 (the lower mask) flip the array then do the same as above
a = np.add.accumulate(np.flip(canny_output,0)/255,0)
a = np.clip(a, 0, 2)
a = a%2 * 255
mask2 = cv.convertScaleAbs(np.flip(a,0))

这会返回几乎相同的结果。面具的边框有点不一样...

【讨论】:

  • 第一个解决方案很有效,谢谢!我也会尝试应用np.add.accumulate 方法。
猜你喜欢
  • 2023-01-28
  • 2019-07-09
  • 1970-01-01
  • 2014-06-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-14
  • 1970-01-01
相关资源
最近更新 更多