簡體   English   中英

使用 Pillow 將矩形映射到四邊形

[英]Mapping a rectangle to a quad with Pillow

我正在嘗試編寫一個 Python 程序,該程序采用輸入圖像(例如 JPEG)並生成“地球組件” output 圖像,類似於le Paper Globe 從本質上講,如果 output 圖像經過打印、切割、折疊和粘合,應該得到投影到粗糙球體上的原始圖像。

該程序會將輸入圖像划分為 32 個(8 個水平,4 個垂直)矩形,然后將每個矩形 map 划分為一些精心挑選的梯形,或者更一般地說,四邊形。 我找到了一種將四邊形映射到正方形的 Pillow/PIL 方法,但找不到將矩形 map 映射到四邊形的方法。

有誰知道如何將輸入圖像的矩形 map 到 Python 中的 output 圖像的四邊形上? 我偏愛 Pillow/PIL,但任何可以打開和保存 JPEG 的庫都可以。

基本上,你需要一些透視變換來實現這一點。 Pillow 有Image.transform 您需要事先計算所有必要的參數,即單應變換,參見。 這個問答 我個人會使用 OpenCV 的warpPerspective ,並使用getPerspectiveTransform獲取變換矩陣,這樣您只需要在源圖像中提供四個點,在目標圖像中提供四個點。 這個其他問答有一個很好的快速開始。

在我們詳細介紹 go 之前,我只是想確定一下,以下是您想要實現的目標:

輸出

因此,完整的算法將是:

  1. 加載您的源圖像,以及專用的 output 圖像,它有一些使用 Pillow 的四邊形。 我假設一個白色背景上的黑色四邊形。
  2. 將圖像轉換為 NumPy arrays 以便能夠與 OpenCV 一起使用。
  3. 設置源點。 這些只是您感興趣的區域 (ROI) 的角落。
  4. 查找 - 或知道 - 目的地點。 這些是四邊形的角落。 自動找到這些可能會變得非常困難,因為順序必須與為 ROI 點設置的順序相同。
  5. 獲取變換矩陣,並應用實際的透視變換。
  6. 將扭曲圖像的所需部分復制到初始 output 圖像的四邊形。
  7. 轉換回一些枕頭圖像並保存。

而且,這是完整的代碼,包括一些可視化:

import cv2
import numpy as np
from PIL import Image, ImageDraw

# Input image to get rectangle (region of interest, roi) from
image = Image.open('path/to/your/image.png')
roi = ((100, 30), (300, 200))

# Dummy output image with some quad to paste to
output = Image.new('RGB', (600, 800), (255, 255, 255))
draw = ImageDraw.Draw(output)
draw.polygon(((100, 20), (40, 740), (540, 350), (430, 70)), outline=(0, 0, 0))

# Convert images to NumPy arrays for processing in OpenCV
image_cv2 = np.array(image)
output_cv2 = np.array(output)

# Source points, i.e. roi in input image
tl = (roi[0][0], roi[0][1])
tr = (roi[1][0], roi[0][1])
br = (roi[1][0], roi[1][1])
bl = (roi[0][0], roi[1][1])
pts = np.array([bl, br, tr, tl])

# Find (or know) target points in output image w.r.t. the quad
# Attention: The order must be the same as defined by the roi points!
tl_dst = (100, 20)
tr_dst = (430, 70)
br_dst = (540, 350)
bl_dst = (40, 740)
dst_pts = np.array([bl_dst, br_dst, tr_dst, tl_dst])

# Get transformation matrix, and warp image
pts = np.float32(pts.tolist())
dst_pts = np.float32(dst_pts.tolist())
M = cv2.getPerspectiveTransform(pts, dst_pts)
image_size = (output_cv2.shape[1], output_cv2.shape[0])
warped = cv2.warpPerspective(image_cv2, M, dsize=image_size)

# Get mask from quad in output image, and copy content from warped image
gray = cv2.cvtColor(output_cv2, cv2.COLOR_BGR2GRAY)
gray = cv2.threshold(gray, 128, 255, cv2.THRESH_BINARY_INV)[1]
cnts = cv2.findContours(gray, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
mask = np.zeros_like(output_cv2)
mask = cv2.drawContours(mask, cnts, 0, (255, 255, 255), cv2.FILLED)
mask = mask.all(axis=2)
output_cv2[mask, :] = warped[mask, :]

# Transform back to PIL images
output_new = Image.fromarray(output_cv2)
output_new.save('final_output.jpg')

# Just for visualization
import matplotlib.pyplot as plt
draw = ImageDraw.Draw(image)
draw.rectangle(roi, outline=(255, 0, 0), width=3)
plt.figure(0, figsize=(18, 9))
plt.subplot(1, 3, 1), plt.imshow(image), plt.title('Input with ROI')
plt.subplot(1, 3, 2), plt.imshow(output), plt.title('Output with quad')
plt.subplot(1, 3, 3), plt.imshow(output_new), plt.title('Final output')
plt.tight_layout(), plt.show()

在第 4 步,自動查找目標點,您可以執行以下操作:

# Find target points in output image w.r.t. the quad
gray = cv2.cvtColor(output_cv2, cv2.COLOR_BGR2GRAY)
gray = cv2.threshold(gray, 128, 255, cv2.THRESH_BINARY_INV)[1]
cnts = cv2.findContours(gray, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
approx = cv2.approxPolyDP(cnts[0], 0.03 * cv2.arcLength(cnts[0], True), True)

這基本上是在圖像中找到輪廓,並逼近角落。 您仍然需要找到結果點的正確順序...

----------------------------------------
System information
----------------------------------------
Platform:      Windows-10-10.0.16299-SP0
Python:        3.8.5
Matplotlib:    3.3.3
NumPy:         1.19.5
OpenCV:        4.5.1
Pillow:        8.1.0
----------------------------------------

暫無
暫無

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

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