簡體   English   中英

如何拉伸一條線以適應 Python OpenCV 的圖像?

[英]How to stretch a line to fit image with Python OpenCV?

我有一個大小為W * H的圖像,我想在上面畫一條線,這條線應該自動適合圖像,例如,如果我畫它:

在此處輸入圖像描述

我要這個:

在此處輸入圖像描述

我如何在 Python 和 OpenCV 中執行此操作? 謝謝

方法#1:只畫延長線(無坐標)

之前->之后

這是 function 當給定點p1p2時,只會繪制延長線。 默認情況下,線條會被圖像邊界剪裁。 還有一個distance參數,用於確定從原始起點繪制多遠或直到線條到達圖像邊界。 如果您需要新的(x1, y1)(x2, y2)坐標,請參閱第 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()

方法#2:帶坐標的全圖

如果您需要新的(x1, y1)(x2, y2)坐標,它會變得有點復雜,因為我們需要為每種可能的情況計算生成的新點。 可能的情況是水平、垂直、正斜率、負斜率和精確對角線。 這是具有新的兩個坐標點的每種情況的結果:白色是原始線,綠色是延長線

垂直的

(250, 0) (250, 500)

水平的

(0, 300) (500, 300)

正斜率

(0, 450) (450, 0)

負斜率

(0, 142) (500, 428)

左角對角線

(0, 0) (500, 500)

右角對角線

(0, 0) (500, 500)

代碼

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()

我假設您的意思如下:您有 2 個點 - 例如圖像中的p1p2 您不需要在 p1 和 p2 之間畫一條線,而是希望包含這些點但一直延伸到圖像邊緣的線。

您將需要處理幾種情況,具體取決於線應該到達的邊緣。 這取決於原始線段的位置和角度。

下面是一個代碼示例,說明如何處理其中一種情況 - 線條應到達頂部和右側邊緣。 您將需要對其進行修改以類似地處理其他情況,並確定應應用哪種情況。

import cv2
import numpy as np

# At the moment only a line that hits the top and right edges is handled.
# You need to handle the rest of the cases in a similar way.
def draw_line_fit_topright(img, p1, p2, color, thinkness):
    h = img.shape[0]
    w = img.shape[1]
    dx = float(p2[0]-p1[0])
    dy = float(p2[1]-p1[1])
    p1new = (int(p1[0] - dx/dy*p1[1]), 0)
    p2new = (w-1, int(p2[1] + dy/dx*(w-p2[0]-1)))
    cv2.line(img, p1new, p2new, color, thickness=thinkness)
    cv2.line(img, p1, p2, (0,0,255), thickness=thinkness-3)

w = 1024
h = 768
p1 = (240,50)
p2 = (440,150)
img1 = np.zeros((h, w, 3), dtype = "uint8")
draw_line_fit_topright(img1, p1, p2, (255, 0, 0), 5)
cv2.imshow("img1", img1)
cv2.waitKey(0)

注意:我知道我的代碼並沒有完全解決問題。 但我認為它會引導你朝着正確的方向前進。

據推測,你的線是由它的兩個端點已知的,設PQ (你應該這么說)。 沿着這條線的一般點的坐標由P + t (Q - P)向量給出。 現在您必須與圖像邊緣相交,例如與上 ( Y=0 ) 或下 ( Y=Height-1 ) 側。 這給出了坐標點(X, Y) ,其中

X = Px + (Y - Py) (Qx - Px) / (Qy - Py)

並且您需要檢查是否0 ≤ X < Width 對四個邊重復此操作(在適當的地方切換XY ),最后你會發現兩個點。


這給了你主要的想法。 在實踐中,存在一些極端情況,例如一條垂直線或水平線,或者一條穿過一兩個角的線。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM