【前言】
在我自製「畫家小工具」該過程中,圖片轉線稿是最重要的功能,而生活中在圖像編輯時,人們也常常拓寬邊線形成如貼紙的效果。除上述以外,邊線也是種方便我們尋找物體與看出圖片架構的方式。那我們能透過那些方法找到邊線並加以利用呢?
【預計內容】
- Thresholding
- Canny Algorithm
- Sobel Operator
- Laplacian
- 程式碼提供
【主要內容】
1. Thresholding
Thresholding 故其名,是透過設定像素值的區間保留區塊。這種方法十分不穩定,並且本質上來說不算邊線偵測的方法,但時常使用的前處理方式。舉個例,今天有張黑白斑點的背景,那轉為灰階後只選擇保留中間的數值,我們就能夠減少背景斑點的影響,生成更乾淨的線稿或遮罩(mask)。當然,若前景角色的衣物有深淺色的話也很容易被刪掉,因此去除背景更是一件複雜但重要的功能。
2. Canny Algorithm
坎尼算子先透過高斯模糊進行降噪,經由 Thresholding 與亮度梯度進行比對,找出可能為邊線的部分。那要注意的一點是,你能夠透過高斯模糊將線條簡單化,但模糊效果太強則容易導致找不到邊線,最終回傳全黑的遮罩。
* 右圖先額外經過 (15, 15) 的高斯模糊
3. Sobel Operator
索伯算子有趣的點是,擁有橫向與縱向的捲積,最後再合併起來,從而帶有微小的方向變化。亦是透過梯度的變化來判斷是否為邊線。那在 opencv 中,可以透過 sobel() 方法的 ksize 調整捲積大小,當 ksize 越大則會得到更模糊的結果。
* 左右之 ksize 分別為 3 與 5
4. Laplacian
拉普拉斯算子亦是透過捲積,透過二次微分偵測黑白的急遽色差,之後透過 Zero-Crossing,即可辨別是否為邊緣。同樣,在 opencv 的 Laplacian() 透過 ksize 調整捲積大小,當 ksize 越大會得到更模糊的效果。
* 左右之 ksize 分別為 3 與 5
5. 程式碼提供
import cv2
import numpy as np
import matplotlib.pyplot as plt
# Read the image
image = cv2.imread('demo.png')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# Apply Canny edge detection
edges_canny = cv2.Canny(gray, 50, 150)
# Apply Sobel operator
sobel_x = cv2.Sobel(gray, cv2.CV_64F, 1, 0, ksize=3)
sobel_y = cv2.Sobel(gray, cv2.CV_64F, 0, 1, ksize=3)
edges_sobel = cv2.magnitude(sobel_x, sobel_y)
edges_sobel = np.uint8(edges_sobel)
# Apply Laplacian edge detection
laplacian = cv2.Laplacian(gray, cv2.CV_64F, ksize=3)
edges_laplacian = np.uint8(np.absolute(laplacian))
# Apply thresholding
_, thresh = cv2.threshold(gray, 150, 255, cv2.THRESH_BINARY)
# Plotting
plt.figure(figsize=(12, 10))
# Original Image
plt.subplot(2, 3, 1)
plt.imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
plt.title('Original')
plt.axis('off')
# Grayscale Image
plt.subplot(2, 3, 2)
plt.imshow(gray, cmap='gray')
plt.title('Grayscale')
plt.axis('off')
# Thresholding
plt.subplot(2, 3, 3)
plt.imshow(thresh, cmap='gray')
plt.title('Thresholding')
plt.axis('off')
# Canny Edge Detection
plt.subplot(2, 3, 4)
plt.imshow(edges_canny, cmap='gray')
plt.title('Canny Edge Detection')
plt.axis('off')
# Sobel Edge Detection
plt.subplot(2, 3, 5)
plt.imshow(edges_sobel, cmap='gray')
plt.title('Sobel Edge Detection')
plt.axis('off')
# Laplacian Edge Detection
plt.subplot(2, 3, 6)
plt.imshow(edges_laplacian, cmap='gray')
plt.title('Laplacian Edge Detection')
plt.axis('off')
plt.tight_layout()
plt.show()
【後話】
在這篇文章中,我們講述了線稿的生成方式與不同用途,那在下篇,我們將透過 Canny Algorithm 展示各位,在不同的後製效果下,又會如何影響線稿的生成。
那我們就下期再會啦~
【參考資料】
[1] Ken Huang, “邊緣偵測 – 索伯算子,” Medium, Jan. 22, 2021.
https://medium.com/%E9%9B%BB%E8%85%A6%E8%A6%96%E8%A6%BA/%E9%82%8A%E7%B7%A3%E5%81%B5%E6%B8%AC-%E7%B4%A2%E4%BC%AF%E7%AE%97%E5%AD%90-sobel-operator-95ca51c8d78a
[2] Ken Huang, “邊緣偵測 – 拉普拉斯算子,” Medium, Jan. 22, 2021.
https://medium.com/%E9%9B%BB%E8%85%A6%E8%A6%96%E8%A6%BA/%E9%82%8A%E7%B7%A3%E5%81%B5%E6%B8%AC-%E6%8B%89%E6%99%AE%E6%8B%89%E6%96%AF%E7%AE%97%E5%AD%90-laplacian-operator-ea877f1945a0