【问题标题】:find angle between major axis of ellipse and x-axis of coordinate (help me implement method from paper)找到椭圆的长轴和坐标的 x 轴之间的角度(帮助我实现纸上的方法)
【发布时间】:2021-06-15 15:48:42
【问题描述】:

所以我试图从这个paper 实现一个方法。我被困在必须找到病变最佳拟合椭圆的长轴与坐标系的 x 轴之间的角度的部分。

这是示例图片:

这是我目前得到的:

有可能找到那个角度吗?找到角度后,我必须将 RoI 沿 x 轴翻转该角度。

更新 ----------

到 Roi 图片的 Google 驱动器链接:RoI image

基于paper分步实现方法。

首先,我应该将 RoI 重新定位到图像坐标的中心。在论文中,他们使用其质心将 RoI 居中。我设法根据在answer 中找到的这段代码来做到这一点。如果我的 RoI 很小并且不触及图像边框,结果很好。但如果我有大图像,结果真的很糟糕。所以我最终使用 boundingRect 使 RoI 居中。这是居中的结果:

将 RoI 居中的代码:

import math

import cv2
import numpy as np
import matplotlib.pyplot as plt

# read image
cont_img = cv2.imread(r"C:\Users\Pandu\Desktop\IMD064_lesion.bmp", 0)
cont_rgb = cv2.cvtColor(cont_img, cv2.COLOR_GRAY2RGB)

# fit ellipse and find ellipse properties
hh, ww = cont_img.shape
contours, hierarchy = cv2.findContours(cont_img, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
ellipse = cv2.fitEllipse(contours[0])
(xc, yc), (d1, d2), angle = ellipse

# centering by centroid
half_width = int(ww/2)
half_height = int(hh/2)
offset_x = (half_width-xc)
offset_y = (half_height-yc)

T = np.float32([[1, 0, offset_x], [0, 1, offset_y]])
centered_by_centroid = cv2.warpAffine(cont_img.copy(), T, (ww, hh))
plt.imshow(centered_by_centroid, cmap=plt.cm.gray)

# centering by boundingRect
# This centered RoI is (L)
x, y, w, h = cv2.boundingRect(contours[0])
startx = (ww - w)//2
starty = (hh - h)//2
centered_by_boundingRect = np.zeros_like(cont_img)
centered_by_boundingRect[starty:starty+h, startx:startx+w] = cont_img[y:y+h, x:x+w]
plt.imshow(centered_by_boundingRect, cmap=plt.cm.gray)

第二,在将RoI居中后,我应该找到方向角度并根据该角度旋转RoI,然后翻转。使用来自 answer 的代码。 (这是旋转 RoI 的正确方法吗?):

# find ellipse properties of centered RoI
contours, hierarchy = cv2.findContours(centered_by_boundingRect, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
ellipse = cv2.fitEllipse(contours[0])
(xc, yc), (d1, d2), angle = ellipse
roi_centroid = (xc, yc)

rot_angle = 90 - angle
if rot_angle < 0:
    rot_angle += 180

# This rotated RoI is (Lx)
M = cv2.getRotationMatrix2D(roi_centroid, -rot_angle, 1.0)
rot_im = cv2.warpAffine(centered_by_boundingRect, M, (ww, hh))
plt.imshow(rot_im, cmap=plt.cm.gray)

# (Ly)
# by passing 0 to flip() should flip image around x-axis, but I get the same result as the paper
res_flip_y = cv2.flip(rot_im.copy(), 0)
plt.imshow(res_flip_y , cmap=plt.cm.gray)

# (L) (xor) (Lx)
res_x_xor = cv2.bitwise_xor(centered_by_boundingRect, rot_im)
plt.imshow(res_x_xor, cmap=plt.cm.gray)

# (L) (xor) (Ly)
res_y_xor = cv2.bitwise_xor(centered_by_boundingRect, res_flip_x)
plt.imshow(res_y_xor, cmap=plt.cm.gray)

我仍然无法得到与论文相同的结果,旋转操作在大 RoI 上也会产生不好的结果。帮助...

更新 ---------- 20/03/2021

Small RoI:旋转结果很好,看起来与论文相似,但在 L (xor) Lx 或 L (xor) Ly 上仍然没有得到相同的最终结果

大投资回报率:随着投资回报率超出边界/图像,旋转结果不佳

【问题讨论】:

  • 旋转操作产生不好的结果是什么意思?你能张贴一张你期望看到的照片吗?对我来说,它似乎旋转得很好。
  • @Ian 非常感谢您的回复...问题描述已更新...
  • 如果问题是白色斑点从图像边缘伸出,您可以在图像上添加黑色边框,使其在旋转时足以容纳斑点。您可以使用 cv2.copyMakeBorder() 添加边框

标签: python opencv image-processing


【解决方案1】:

您正在寻找的角度是从 fitEllipse 返回的。它只是根据不同的参考系旋转了一点。您可以通过做 90 角来获得逆时针旋转角度。至于旋转 roi,您可以使用 minAreaRect 直接获得最小拟合矩形,也可以将边界框拟合到轮廓并单独旋转每个点。

绿色矩形是 minAreaRect(),红色矩形是旋转后的 boundingRect()。

import cv2
import numpy as np
import math

# rotate point
def rotate2D(point, deg):
    rads = math.radians(deg);
    x, y = point;
    rcos = math.cos(rads);
    rsin = math.sin(rads);
    rx = x * rcos - y * rsin;
    ry = x * rsin + y * rcos;
    rx = round(rx);
    ry = round(ry);
    point[0] = rx;
    point[1] = ry;

# translate point
def translate2D(src, target, sign):
    tx, ty = target;
    src[0] += tx * sign;
    src[1] += ty * sign;

# read image
cont_img = cv2.imread("blob.png", 0)
cont_rgb = cv2.cvtColor(cont_img, cv2.COLOR_GRAY2RGB)

# find contour
_, contours, hierarchy = cv2.findContours(cont_img, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)

# fit ellipse and get ellipse properties
ellipse = cv2.fitEllipse(contours[0])
(xc, yc), (d1, d2), angle = ellipse

# -------- NEW STUFF IN HERE --------------
# calculate counter-clockwise angle relative to x-axis
rot_angle = 90 - angle;
if rot_angle < 0:
    rot_angle += 180;
print(rot_angle);

# if you want a rotated ROI I would recommend using minAreaRect rather than rotating a different rectangle
# fit a minrect to the image # this is taken directly from OpenCV's tutorials
rect = cv2.minAreaRect(contours[0]);
box = cv2.boxPoints(rect);
box = np.int0(box);
cv2.drawContours(cont_rgb, [box], 0, (0,255,0), 2);

# but if you really want to use a different rectangle and rotate it, here's how to do it
# create rectangle
x,y,w,h = cv2.boundingRect(contours[0]);
rect = [];
rect.append([x,y]);
rect.append([x+w,y]);
rect.append([x+w,y+h]);
rect.append([x,y+h]);

# rotate it
rotated_rect = [];
center = [x + w/2, y + h/2];
for point in rect:
    # for each point, center -> rotate -> uncenter
    translate2D(point, center, -1);
    rotate2D(point, 90 - rot_angle); # "90 - angle" is because rotation goes clockwise
    translate2D(point, center, 1);
    rotated_rect.append([point]);
rotated_rect = np.array(rotated_rect);
cv2.drawContours(cont_rgb, [rotated_rect.astype(int)], -1, (0,0,255), 2);
# ------------- END OF NEW STUFF -----------------


# draw fitted ellipse and centroid
target_ellipse = cv2.ellipse(cont_rgb.copy(), ellipse, (37, 99, 235), 10)
centroid = cv2.circle(target_ellipse.copy(), (int(xc), int(yc)), 20, (250, 204, 21), -1)

# draw major axis
rmajor = max(d1, d2)/2

if angle > 90:
    angle = angle - 90
else:
    angle = angle + 90

xtop_major = xc + math.cos(math.radians(angle))*rmajor
ytop_major = yc + math.sin(math.radians(angle))*rmajor
xbot_major = xc + math.cos(math.radians(angle+180))*rmajor
ybot_major = yc + math.sin(math.radians(angle+180))*rmajor
top_major = (int(xtop_major), int(ytop_major))
bot_major = (int(xbot_major), int(ybot_major))

target_major_axis = cv2.line(centroid.copy(),
                             top_major, bot_major,
                             (0, 255, 255), 5)

## image center coordinate
hh, ww = target_major_axis.shape[:2];
x_center_start = (0, int(hh/2))
x_center_end = (int(ww), int(hh/2))
y_center_start = (int(ww/2), 0)
y_center_end = (int(ww/2), int(hh))
img_x_middle_coor = cv2.line(target_major_axis.copy(), x_center_start, x_center_end, (219, 39, 119), 10)
img_y_middle_coor = cv2.line(img_x_middle_coor.copy(), y_center_start,
                             y_center_end, (190, 242, 100), 10)

# show
cv2.imshow("image", img_y_middle_coor);
cv2.waitKey(0);

为了将来:在将代码粘贴到此处之前,请检查您的代码是否运行。除了缺少“导入”行之外,它还缺少这一行:

hh, ww = target_major_axis.shape[:2]

如果您粘贴的示例代码有错误,那么每个想要提供帮助的人都必须浪费一些时间来解决错误,然后才能开始研究解决方案。

【讨论】:

  • 您好 @Ian Chu 先生,非常感谢您的帮助,我很抱歉缺少代码。我仍然无法旋转 RoI,而且我仍然无法获得与论文相同的最终结果。您能否请检查我更新的问题...这里真的需要一些帮助:(
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-13
  • 1970-01-01
  • 2016-05-07
  • 2011-02-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多