简体   繁体   中英

Opencv and python - Shading area between two lines

I've got some working code, but performance is awful and it can be done better, so lets see how it can be optimized.

I have an image with two semi-vertical lines drawn and would like to shade the area between those two lines. My current code iterates pixel by pixel, which is painfully slow in python.

#Get starting images
shade_image = np.zeros_like(image)
line_image = plot_lines(shade_image, left_line, right_line)
#Choose color
shade_color = [0, 255, 0]
#Iterate through and fill blank image
left_switch = False
right_switch = False
for i in range(0, image.shape[0]):
    left_switch = False
    right_switch = False
    for j in range(0, image.shape[1]):
        if left_switch & right_switch: shade_image[i][j] = shade_color
        #Find left edge of left line
        if (not left_switch) & (not right_switch) & np.any(line_image[i][j] != 0):
            left_switch = True
        #Find right edge of left line
        elif left_switch & (not right_switch) & np.all(line_image[i][j] == 0):
            right_switch = True
        #Find left edge of left line
        elif left_switch & right_switch & np.any(line_image[i][j] != 0):
            break
        #In case of a single line, no shading
        elif left_switch & right_switch & (j == (image.shape[1] - 1)):
             shade_image[i].fill(0)
#Shade lane area
output_image = cv2.addWeighted(image, 1.0, shade_image, 1.0, gamma=0.0)

Not pretty. I'm thinking somehow this can be done by using all the nonzeros in the line_image and creating a mask, but I don't know how to fill between the left and right points. I tried using fillPolygon but I don't really know how to convert all the nonzero's into vertices.

If it helps, the shading can also incorporate the lines, this isn't exact.


@Alxeander - Very helpful! I'm almost there, except now there is the issue of filling outside of a convex line due to the bounding box, like so: 在此处输入图片说明

Alternatively, I ditched the cv2.convexHull and used the concatenated points directly as vertices of a polygon and got this result: 在此处输入图片说明

Which follows the curvature, convex or concave, however because of the order of the points it flips when it goes left to right. I've tried using np.flips(points2, 0) but no change. I'll play with it some more, almost there.


Made a mistake with the np.flip(points2, 0), that worked!: 在此处输入图片说明

Thanks for the help!

Since your lines are defined by a polynomial, that means you can easily get the point set from them. You have your domain values x and your polynomial values y . If you put these into a point array

points = np.array([[[x1, y1]], ..., [[xn, yn]]])

then you can pass these points into the OpenCV function cv2.fillPoly() or cv2.drawContours() to fill in the region.

Note that polynomials can easily define lines way outside your image bounds. In order to plot a contour or polygon you should use closed shapes, and it will be easier to cut those excess points out of your domain.

Here's a full example defining a region based on two polynomials and filling the region:

import numpy as np 
import cv2

img = cv2.imread('img1.png')
h, w = img.shape[:2]

x = np.arange(w)
polynomial1 = lambda x: x**2/800
polynomial2 = lambda x: x+200
y1 = polynomial1(x)
y2 = polynomial2(x)

points1 = np.array([[[xi, yi]] for xi, yi in zip(x, y1) if (0<=xi<w and 0<=yi<h)]).astype(np.int32)
points2 = np.array([[[xi, yi]] for xi, yi in zip(x, y2) if (0<=xi<w and 0<=yi<h)]).astype(np.int32)
points2 = np.flipud(points2)
points = np.concatenate((points1, points2))

polynomialgon = img.copy()
cv2.fillPoly(polynomialgon, [points], color=[255,255,255])
cv2.imshow('Polygon defined by two polynomials', polynomialgon)
cv2.waitKey(0)

Resulting in:

由两个多项式定义的填充多边形

Note that the flipud is to keep the points going in a clockwise fashion from the endpoints of the lines; otherwise where polynomial1 ends, the line then goes up to where polynomial2 begins, so we're backtracking and crossing over the region. It doesn't matter what the line does so long as the endpoints of the line are tracked clockwise or anti-clockwise.

Alexander steered me in the right direction, I just had to tweak it for handling convex lines:

import numpy as np 
import cv2

img = cv2.imread('img1.png')
h, w = img.shape[:2]

x = np.arange(w)
polynomial1 = lambda x: x**2/800
polynomial2 = lambda x: x+200
y1 = polynomial1(x)
y2 = polynomial2(x)

points1 = np.array([[[xi, yi]] for xi, yi in zip(x, y1) if (0<=xi<w and 0<=yi<h)]).astype(np.int32)
points2 = np.array([[[xi, yi]] for xi, yi in zip(x, y2) if (0<=xi<w and 0<=yi<h)]).astype(np.int32)
points = np.concatenate(points1, np.flip(points2, 0))

polynomialgon = img.copy()
cv2.fillPoly(polynomialgon, [points], color=[255,255,255])
cv2.imshow('Polygon defined by two polynomials', polynomialgon)
cv2.waitKey(0)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM