【问题标题】:Drawing cartesian axes绘制笛卡尔坐标轴
【发布时间】:2019-06-05 00:14:11
【问题描述】:

我正在处理我自己制作的图像。我已经过滤了图像 (B2W) 以正确检测图像中可见的水射流的轮廓。

我现在要做的是绘制一个 XY 轴(x 轴向左,y 轴向上,原点从轮廓的最右(最低)点开始(我已经检测到 XY 坐标)这个原点)。如果可能,我的 x 轴和 y 轴的间隔需要有一个指定的长度。之后我想在这些指定的间隔检测我的轮廓 I 的平均中心线的 (x,y) 坐标画好了。

另一种方法是:绘制轮廓的上下边缘(绿线)从下边缘到上边缘绘制垂直线,并在x-处确定每个区间的每条线的中点(X,Y)坐标轴。同样,原点应该在最正确的点。

我的问题:绘制 xy 笛卡尔轴,在边缘之间画线(确定中心很容易解决)但确定 (X,Y) 坐标对我来说又是个问题。

请随时为我提供有关此任务的建议,提前致谢

图片示例(已经是黑白的)

![][2]

检测轮廓的代码

import cv2


image = cv2.imread("C:/.../jet.jpg")
blurred = cv2.pyrMeanShiftFiltering(image,1,0.5)
gray = cv2.cvtColor(blurred,cv2.COLOR_BGR2GRAY)
ret , threshold = cv2.threshold(gray,210,20,cv2.THRESH_BINARY+cv2.THRESH_OTSU)

_, contours,_=cv2.findContours(threshold,cv2.RETR_LIST,cv2.CHAIN_APPROX_NONE)



cv2.drawContours(image,contours,-1,(0,0,255),2)  
r = 800.0 / image.shape[1]
dim = (800,  int(image.shape[0] * r))

# perform the actual resizing of the image and show it
resized = cv2.resize(image, dim, interpolation = cv2.INTER_AREA)
cv2.imwrite("C:/.....jpg",resized)

cv2.imshow('Display', resized)
cv2.waitKey(0)
cv2.destroyAllWindows()

检测最右边点的代码

import cv2

im = cv2.imread("C:......jpg"")
gray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY);
gray = cv2.GaussianBlur(gray, (5, 5), 0)
_, bin = cv2.threshold(gray,100,255,1) # inverted threshold (light obj on    dark bg)
bin = cv2.dilate(bin, None)  # fill some holes
bin = cv2.dilate(bin, None)
bin = cv2.erode(bin, None)   # dilate made our shape larger, revert that
bin = cv2.erode(bin, None)
bin, contours, hierarchy = cv2.findContours(bin, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)

rc = cv2.minAreaRect(contours[0])
box = cv2.boxPoints(rc)
for p in box:
pt = (p[0],p[1])
print (pt)
cv2.circle(im,pt,5,(200,0,0),2)
cv2.imshow("extreme_coordinates", im)
cv2.waitKey(0)
cv2.destroyAllWindows()

现在我尝试了不同的方法来绘制 XY 轴,但没有积极的结果。 有人可以帮我完成这部分,如何正确启动?

【问题讨论】:

  • 所以你的问题是在知道原点和已知区间大小的情况下绘制 x 和 y 轴?间隔是像素吗?
  • 对于我的项目,x 轴(和 y 轴)应该是无量纲的(例如,x 轴上的间隔标记(y 轴相同)应该是 1 - 2 - 3 - 。 .. 依此类推,但如果可能,间隔维度应具有以像素为单位的指定长度(下图显示来自水平基础的射流,等于 2 个间隔测量值(绿色轮廓最低部分)
  • (下图显示来自水平底座的射流,等于 2 个间隔测量值(绿色轮廓最低部分)我有不同的喷嘴直径(射流的水平底座),目标是制作与喷嘴直径相同尺寸的间隔,例如,以像素尺寸测量的某个 xy 位置(参考原点)除以像素尺寸的喷嘴直径,x 和 y 轴统一 = 喷嘴直径的无量纲情况跨度>
  • @JonahB 你的意思是像this 这样的东西吗?还是应该先旋转羽流使其先水平?
  • @DanMašek 是的,这就是我正在寻找的解决方案!是否可以调整 X 轴和 Y 轴上标记之间的间隔尺寸?在每个标记处,我还想在 (X,Y coordiantes) 中找到喷气机的中心这也可能吗?我是否需要对射流的下边缘和上边缘进行边缘检测,然后将边缘与与 Y 轴平行的线连接起来,进一步了解每条平行线的起点和终点坐标,使用以下公式 ( Y2-Y1)/2 +Y1 = 中点Y坐标

标签: python


【解决方案1】:

画轴

绘制坐标轴的第一步是找到轮廓的边界矩形。由于羽流的方向,我们可以使用右下角作为图表的原点。 X 轴将成为左下角和原点之间的连线,Y 轴将成为右上角和原点之间的连线。

或者,这些线可以稍微延伸超过左下角和右上角,并在其末端绘制箭头(每个使用 2 条短线)。

要确定刻度的位置,我们只需从原点开始,将 X 或 Y 坐标减小固定步长,直到到达边界框的左下角或右上角。

知道位置后,我们可以将刻度画成垂直于轴的短线。


完整的脚本:

import cv2
import numpy as np

# Refactored original code

def find_plume_image(image):
    blurred = cv2.pyrMeanShiftFiltering(image, 1, 0.5)
    gray = cv2.cvtColor(blurred, cv2.COLOR_BGR2GRAY)
    _, threshold = cv2.threshold(gray, 210, 20, cv2.THRESH_BINARY+cv2.THRESH_OTSU)

    _, contours,_=cv2.findContours(threshold, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)

    cv2.drawContours(image, contours, -1, (0,0,255), 1)  
    r = 800.0 / image.shape[1]
    dim = (800,  int(image.shape[0] * r))

    return cv2.resize(image, dim, interpolation = cv2.INTER_AREA)

def get_plume_contour(plume_image):
    gray = cv2.cvtColor(plume_image,cv2.COLOR_BGR2GRAY);
    gray = cv2.GaussianBlur(gray, (5, 5), 0)
    _, bin = cv2.threshold(gray, 100, 255, cv2.THRESH_BINARY_INV)
    bin = cv2.dilate(bin, None, iterations=2)  # fill some holes
    bin = cv2.erode(bin, None, iterations=2)   # dilate made our shape larger, revert that
    _, contours, _ = cv2.findContours(bin, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
    return contours[0]

# Drawing the axes

def get_tick_columns(plume_contour, interval):
    x,y,w,h = cv2.boundingRect(plume_contour)
    return range(x+w-1, x-1, -interval)

def get_tick_rows(plume_contour, interval):
    x,y,w,h = cv2.boundingRect(plume_contour)
    return range(y+h-1, y-1, -interval)

def draw_arrow_tip(image, point, size, color, horizontal):
    if horizontal:
        tips = [(point[0]+size, point[1]+size)
            , (point[0]+size, point[1]-size)]
    else:
        tips = [(point[0]+size, point[1]+size)
            , (point[0]-size, point[1]+size)]

    for tip in tips:
        cv2.line(image, point, tip, color, 1)

def draw_ticks(image, origin, positions, size, color, horizontal):
    for i in positions:
        if horizontal:
            p1 = (i, origin[1]-(size>>1))
            p2 = (p1[0], p1[1]+size)
        else:
            p1 = (origin[0]-(size>>1), i)
            p2 = (p1[0]+size, p1[1])

        cv2.line(image, p1, p2, color, 1)

def draw_axes(output_image, plume_contour, interval, tick_size):
    EXTENSION = 15 # Amount to extend axis line to provision for end arrows
    ARROW_SIZE = 5 # X and Y offset for drawing the end arrow
    AXES_COLOR = (255,127,127)

    x,y,w,h = cv2.boundingRect(plume_contour)
    origin = (x+w-1,y+h-1)
    bottom_left = (x-EXTENSION, origin[1])
    top_right = (origin[0], y-EXTENSION)

    # X axis
    cv2.line(output_image, origin, bottom_left, AXES_COLOR, 1)
    draw_arrow_tip(output_image, bottom_left, ARROW_SIZE, AXES_COLOR, True)
    draw_ticks(output_image, origin, get_tick_columns(plume_contour, interval), tick_size, AXES_COLOR, True)

    # Y axis
    cv2.line(output_image, origin, top_right, AXES_COLOR, 1)
    draw_arrow_tip(output_image, top_right, ARROW_SIZE, AXES_COLOR, False)
    draw_ticks(output_image, origin, get_tick_rows(plume_contour, interval), tick_size, AXES_COLOR, False)

    return output_image

# ---------------------------        

TICK_SPACING = 10

image = cv2.imread('plume.jpg')
plume_image = find_plume_image(image)
plume_contour = get_plume_contour(plume_image)

output = draw_axes(plume_image.copy(), plume_contour, TICK_SPACING, 11)

cv2.imwrite('plume_axes.jpg', output)

示例输出:


确定羽流中心线

实现这一点的一个相对简单的方法是首先将羽流轮廓绘制到一个空白的单通道图像中,并用白色填充它。然后对于每一列感兴趣的列(例如 X 轴刻度所在的列),我们可以找到所有非零像素的位置,并从结果中选择最小和最大 Y 坐标。这将为我们提供顶部和底部边缘的位置。中点是这两个值的平均值。


代码:

(继续上一个脚本)

def get_plume_limits(plume_contour, columns):
    x,y,w,h = cv2.boundingRect(plume_contour)
    temp_image = np.zeros((y+h, x+w), np.uint8)
    cv2.drawContours(temp_image, [plume_contour], -1, 255, -1)

    limits = {}
    for i in columns:
        positions = np.nonzero(temp_image[:,i])[0]
        if len(positions) > 0:
            limits[i] = (positions.min(), positions.max())

    return limits

def draw_plume_limits(output_image, plume_limits):
    for x, limit in plume_limits.iteritems():
        cv2.circle(output_image, (x, limit[0]), 2, (255, 0, 255), -1)
        cv2.circle(output_image, (x, limit[1]), 2, (0, 255, 255), -1)
        cv2.circle(output_image, (x, (limit[0]+limit[1])>>1), 2, (0, 127, 0), -1)

    return output_image

plume_limits = get_plume_limits(plume_contour, get_tick_columns(plume_contour, TICK_SPACING))
draw_plume_limits(output, plume_limits)

cv2.imwrite('plume_axes_limits.jpg', output)

示例输出:

【讨论】:

  • @JonahB 没问题。如果您认为它有用,请随时投票(这意味着单击答案顶部的小向上箭头)这个答案,并将其标记为已接受。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-11-27
  • 1970-01-01
相关资源
最近更新 更多