07.05 如何用Python實現神奇切圖算法Seam Carving?

在第 34 屆 SIGGRAPH 2007 數字圖形學年會上,以色列的兩位教授 Shai Avidan 和 Ariel Shamir 展示了一種新的縮放裁剪圖像方法,利用這個技術我們可以在縮放時固定圖片中特定區域的大小,或者可以在縮小時讓特定的區塊被周圍圖像縫合消除使圖片縮放後仍然維持整體的完整性。

如何用Python實現神奇切圖算法Seam Carving?

變為:

如何用Python實現神奇切圖算法Seam Carving?

以所舉的例子為素材,重點講講如何用Python基本實現接縫剪裁算法。

為了執行縫隙拼接,我們需要兩個重要的輸入:

· 1.原始圖片:我們想要調整大小的圖片。

· 2.能量圖(energy map): 我們從原始圖像導出的能量圖。

算法工作過程如下所示:

00001. 為每個像素分配一個能量值

00002. 找到能量值最小的像素的八連通路徑

00003. 刪除路徑中的所有像素

00004. 重複前面1-3步,直到刪除的行/列數量達到理想狀態

下面是我們需要導入的環境依賴:

import sys

import numpy as npfrom imageio

import imread, imwritefrom scipy.ndimage.filters import convolve

# tqdm並非必需,但能為提供很美觀的進度條,方便我們查看進度from tqdm import trange

開始計算能量圖:

def calc_energy(img):

filter_du = np.array([

[1.0, 2.0, 1.0],

[0.0, 0.0, 0.0],

[-1.0, -2.0, -1.0],

])

# 這會將它從2D濾波轉換為3D濾波器

# 為每個通道:R,G,B複製相同的濾波器

filter_du = np.stack([filter_du] * 3, axis=2)

filter_dv = np.array([

[1.0, 0.0, -1.0],

[2.0, 0.0, -2.0],

[1.0, 0.0, -1.0],

])

# 這會將它從2D濾波轉換為3D濾波器

# 為每個通道:R,G,B複製相同的濾波器

filter_dv = np.stack([filter_dv] * 3, axis=2)

img = img

.astype('float32')

convolved = np.absolute(convolve(img, filter_du)) + np.absolute(convolve(img, filter_dv))

# 我們將紅,綠,藍通道中的能量相加

energy_map = convolved.sum(axis=2)

return energy_map

找到能量值最小的縫隙,創建一個稱為 M 的 2D 數組,存儲該像素上可見的最小能量值。

def minimum_seam(img):

r, c, _ = img.shape

energy_map = calc_energy(img)

M = energy_map.copy()

backtrack = np.zeros_like(M, dtype=np.int)

for i in range(1, r):

for j in range(0, c):

# 處理圖像的左側邊緣,確保我們不會索引-1

if j == 0:

idx = np.argmin(M[i - 1, j:j + 2])

backtrack[i, j] = idx + j

min_energy = M[i - 1, idx + j]

else:

idx = np.argmin(M[i - 1, j - 1:j + 2])

backtrack[i, j] = idx + j - 1

min_energy = M[i - 1, idx + j - 1]

M[i, j] += min_energy

return M, backtrack

從具有最小能量值的縫隙中刪除像素

然後我們移除具有最小能量值的縫隙,返回一個新圖像:

def carve_column(img):

r, c, _ = img.shape

M, backtrack = minimum_seam(img)

# 創建一個(r,c)矩陣,填充值為True

# 後面會從值為False的圖像中移除所有像素

mask = np.ones((r, c), dtype=np.bool)

# 找到M的最後一行中的最小元素的位置

j = np.argmin(M[-1])

for i in reversed(range(r)):

# 標記出需要刪除的像素

mask[i, j] = False

j = backtrack[i, j]

# 因為圖像有3個通道,我們將蒙版轉換為3D

mask = np.stack([mask]

* 3, axis=2)

# 刪除蒙版中所有標記為False的像素,

# 將其大小重新調整為新圖像的維度

img = img[mask].reshape((r, c - 1, 3))

return img

在每一列重複此項操作

def crop_c(img, scale_c):

r, c, _ = img.shape

new_c = int(scale_c * c)

for i in trange(c -

new_c): # use range if you don't want to use tqdm

img = carve_column(img)

return img

彙總信息

我們可以添加一個主函數,從如下命令行調用該函數:

def main():

scale = float(sys.argv[1])

in_filename = sys.argv[2]

out_filename = sys.argv[3]

img = imread(in_filename)

out = crop_c(img, scale)

imwrite(out_filename, out)

if __name__ == '__main__':main()

然後用如下代碼運行:

python carver.py 0.5 image.jpg cropped.jpg

現在,cropped.jpg 應該包含如下一張圖:

如何用Python實現神奇切圖算法Seam Carving?

這樣我們就用 Python 實現了接縫剪裁算法!

那麼行呢?

很簡單,只需旋轉一下圖像,運行 crop_c 就 ok 了!

def crop_r(img, scale_r):

img = np.rot90(img, 1, (0, 1))

img = crop_c(img, scale_r)

img = np.rot90(img, 3, (0, 1))

return img

將如下內容添加至主函數,現在我們也能剪裁行了!

def main():

if len(sys.argv) != 5:

print('usage: carver.py <scale> <image> <image>', file=

sys.stderr)/<image>/<image>/<scale>

sys.exit(1)

which_axis = sys.argv[1]

scale = float(sys.argv[2])

in_filename = sys.argv[3]

out_filename = sys.argv[4]

img = imread(in_filename)

if which_axis == 'r':

out = crop_r(img, scale)

elif which_axis

== 'c':

out = crop_c(img, scale)

else:

print('usage: carver.py <scale> <image> <image>', file=sys.stderr)/<image>/<image>/<scale>

sys.exit(1)

imwrite(out_filename, out)

以如下代碼運行:

python carver.py r 0.5 image2.jpg cropped.jpg

學會了嗎,喜歡的話幫小編評論加關注哦謝謝啦,更多精彩內容請關注百戰程序員!

如何用Python實現神奇切圖算法Seam Carving?


分享到:


相關文章: