Question: I have an image with the size of W * H, I want to draw a line on this and the line should be automatically fit to the image, for example if I draw it:  I want this:  How can I do this in Python and OpenCV? Thanks

Answer: Method #1: Just drawing the extended line (no coordinates) Before -> After   Here's a function when given points p1 and p2, will only draw the extended line. By default, the line is clipped by the image boundaries. There is also a distance parameter to determine how far to draw from the original starting point or until the line hits the border of the image. If you need the new (x1, y1) and (x2, y2) coordinates, see section #2 import cv2 import numpy as np """ @param: p1 - starting point (x, y) @param: p2 - ending point (x, y) @param: distance - distance to extend each side of the line """ def extend_line(p1, p2, distance=10000): diff = np.arctan2(p1[1] - p2[1], p1[0] - p2[0]) p3_x = int(p1[0] + distance*np.cos(diff)) p3_y = int(p1[1] + distance*np.sin(diff)) p4_x = int(p1[0] - distance*np.cos(diff)) p4_y = int(p1[1] - distance*np.sin(diff)) return ((p3_x, p3_y), (p4_x, p4_y)) # Create blank black image using Numpy original = np.zeros((500,500,3), dtype=np.uint8) image1 = original.copy() p1 = (250, 100) p2 = (375, 250) cv2.line(image1, p1, p2, [255,255,255], 2) # Second image, calculate new extended points image2 = original.copy() p3, p4 = extend_line(p1, p2) cv2.line(image2, p3, p4, [255,255,255], 2) cv2.imshow('image1', image1) cv2.imshow('image2', image2) cv2.waitKey()  Method #2: Full drawing with coordinates If you need the new (x1, y1) and (x2, y2) coordinates, it gets a little more complicated since we need to calculate the resulting new points for each possible case. The possible cases are horizontal, vertical, positively sloped, negatively sloped, and exact diagonals. Here's the result for each of the cases with the new two coordinate points: white is the original line and the green is the extended line Vertical  (250, 0) (250, 500)  Horizontal  (0, 300) (500, 300)  Positive slope  (0, 450) (450, 0)  Negative slope  (0, 142) (500, 428)  Left corner diagonal  (0, 0) (500, 500)  Right corner diagonal  (0, 0) (500, 500)  Code import numpy as np import cv2 import math """ @param: dimensions - image shape from Numpy (h, w, c) @param: p1 - starting point (x1, y1) @param: p2 - ending point (x2, y2) @param: SCALE - default parameter to ensure that extended lines go through borders """ def extend_line(dimensions, p1, p2, SCALE=10): # Calculate the intersection point given (x1, y1) and (x2, y2) def line_intersection(line1, line2): x_diff = (line1[0][0] - line1[1][0], line2[0][0] - line2[1][0]) y_diff = (line1[0][1] - line1[1][1], line2[0][1] - line2[1][1]) def detect(a, b): return a[0] * b[1] - a[1] * b[0] div = detect(x_diff, y_diff) if div == 0: raise Exception('lines do not intersect') dist = (detect(*line1), detect(*line2)) x = detect(dist, x_diff) / div y = detect(dist, y_diff) / div return int(x), int(y) x1, x2 = 0, 0 y1, y2 = 0, 0 # Extract w and h regardless of grayscale or BGR image if len(dimensions) == 3: h, w, _ = dimensions elif len(dimensions) == 2: h, w = dimensions # Take longest dimension and use it as maxed out distance if w > h: distance = SCALE * w else: distance = SCALE * h # Reorder smaller X or Y to be the first point # and larger X or Y to be the second point try: slope = (p2[1] - p1[1]) / (p1[0] - p2[0]) # HORIZONTAL or DIAGONAL if p1[0] <= p2[0]: x1, y1 = p1 x2, y2 = p2 else: x1, y1 = p2 x2, y2 = p1 except ZeroDivisionError: # VERTICAL if p1[1] <= p2[1]: x1, y1 = p1 x2, y2 = p2 else: x1, y1 = p2 x2, y2 = p1 # Extend after end-point A length_A = math.sqrt((x2 - x1)**2 + (y2 - y1)**2) p3_x = int(x1 + (x1 - x2) / length_A * distance) p3_y = int(y1 + (y1 - y2) / length_A * distance) # Extend after end-point B length_B = math.sqrt((x1 - x2)**2 + (y1 - y2)**2) p4_x = int(x2 + (x2 - x1) / length_B * distance) p4_y = int(y2 + (y2 - y1) / length_B * distance) # -------------------------------------- # Limit coordinates to borders of image # -------------------------------------- # HORIZONTAL if y1 == y2: if p3_x < 0: p3_x = 0 if p4_x > w: p4_x = w return ((p3_x, p3_y), (p4_x, p4_y)) # VERTICAL elif x1 == x2: if p3_y < 0: p3_y = 0 if p4_y > h: p4_y = h return ((p3_x, p3_y), (p4_x, p4_y)) # DIAGONAL else: A = (p3_x, p3_y) B = (p4_x, p4_y) C = (0, 0) # C-------D D = (w, 0) # |-------| E = (w, h) # |-------| F = (0, h) # F-------E if slope > 0: # 1st point, try C-F side first, if OTB then F-E new_x1, new_y1 = line_intersection((A, B), (C, F)) if new_x1 > w or new_y1 > h: new_x1, new_y1 = line_intersection((A, B), (F, E)) # 2nd point, try C-D side first, if OTB then D-E new_x2, new_y2 = line_intersection((A, B), (C, D)) if new_x2 > w or new_y2 > h: new_x2, new_y2 = line_intersection((A, B), (D, E)) return ((new_x1, new_y1), (new_x2, new_y2)) elif slope < 0: # 1st point, try C-F side first, if OTB then C-D new_x1, new_y1 = line_intersection((A, B), (C, F)) if new_x1 < 0 or new_y1 < 0: new_x1, new_y1 = line_intersection((A, B), (C, D)) # 2nd point, try F-E side first, if OTB then E-D new_x2, new_y2 = line_intersection((A, B), (F, E)) if new_x2 > w or new_y2 > h: new_x2, new_y2 = line_intersection((A, B), (E, D)) return ((new_x1, new_y1), (new_x2, new_y2)) # Vertical # ------------------------------- # p1 = (250, 100) # p2 = (250, 300) # ------------------------------- # Horizontal # ------------------------------- # p1 = (100, 300) # p2 = (400, 300) # ------------------------------- # Positive slope # ------------------------------- # C-F, C-D # p1 = (50, 400) # p2 = (400, 50) # C-F, E-D # p1 = (50, 400) # p2 = (400, 50) # F-E, E-D # p2 = (250, 400) # p1 = (400, 250) # F-E, C-D # p2 = (250, 400) # p1 = (300, 250) # ------------------------------- # Negative slope # ------------------------------- # C-F, E-D # p1 = (100, 200) # p2 = (450, 400) # C-F, F-E # p2 = (100, 200) # p1 = (250, 400) # C-D, D-E # p1 = (100, 50) # p2 = (450, 400) # C-D, F-E p1 = (100, 50) p2 = (250, 400) # ------------------------------- # Exact corner diagonals # ------------------------------- # p1 = (50,50) # p2 = (300, 300) # p2 = (375, 125) # p1 = (125, 375) # ------------------------------- image = np.zeros((500,500,3), dtype=np.uint8) p3, p4 = extend_line(image.shape, p1, p2) print(p3, p4) cv2.line(image, p3, p4, [255,255,255], 2) cv2.line(image, p1, p3, [36,255,12], 2) cv2.line(image, p2, p4, [36,255,12], 2) cv2.imshow('image', image) cv2.waitKey()