以指定的原点绘制XY轴-从轮廓中的平均线获取XY坐标

时间:2018-11-20 13:34:35

标签: python opencv coordinates contour edge-detection

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

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

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

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

请随时向我提出建议,在此先感谢

图片示例(已为黑白)

检测轮廓的代码

import cv2


image = cv2.imread("C:/Users/Jonathan/Documents/HSBOSTON_IMAGES/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:/Users/Jonathan/Documents/HSBOSTON_IMAGES/jet_contour.jpg",resized)

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

用于检测最右边点的代码

import cv2

im = cv2.imread("C:/Users/Jonathan/Documents/HSBOSTON_IMAGES/jet_contour.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轴,但没有得到正面结果。 有人可以帮我这部分,如何正确启动吗?

这个skecth显示了我的问题陈述 enter image description here

1 个答案:

答案 0 :(得分:0)

绘制轴

绘制轴的第一步是找到轮廓的边界矩形。由于羽的方向,我们可以使用右下角作为图表的原点。 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)

示例输出:

Plume with axes


确定羽流中心线

一个相对简单的方法是先将羽状轮廓绘制成空白的单通道图像,然后用白色填充。然后,对于每个感兴趣的列(例如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)

示例输出:

Plume with axes, edges and midpoints