[Python筆記]畫出圖片的邊界,以 PGM 檔案為例

引入 library

import sys # 讀入檔案用的
import numpy as np # 做矩陣運算用的
import matplotlib.pyplot as plt # 最後畫出圖用的

對 PGM 檔案進行處理

執行方法如下。其中 input.pgm 為輸入的檔名,會在執行後跑出 Original Image 與 Edge Image 的比較圖(以 matplotlib.pyplot 繪製而成),同時在該目錄之下中輸出 input_out.pgm

$ python assignment_2.py input.pgm

開始先將檔案做前置處理。

filename = sys.argv[1] # 讀入檔案名稱

# 沿著每一列讀
with open(filename) as file:
    lines = file.readlines() 

# 忽視註解那一列,通常都是以井字號開頭的
for l in list(lines):
    if (l[0] == "#"):
        lines.remove(l)

# 檢查是否是我們要的檔案格式,也就是 ASCII P2
assert lines[0].strip() == "P2"

# 把接下來的每一個數字讀入矩陣
origin_data = []
for line in lines[1:]:
    origin_data.extend([int(c) for c in line.split()])  

# 讀入的數字,第一個是該圖片的 width
width = origin_data[0] 

# 讀入的數字,第二個是該圖片的 height
height = origin_data[1]

# 讀入的數字,第三個是該圖片的 max value
maxval = origin_data[2]

# 然後剩下的都是灰階資料了,同時依照 height 和 width 轉成矩陣
gray_2d_arr = np.array(origin_data[3:]).reshape((height, width))

# 設定邊界與非邊界的臨界值,這個值可以調整,這邊取 256 的一半
epsilon = 128 

開始計算出邊界囉

我們的算法如下所示,對於一個矩陣而言,

$$ \left[ \begin{array}{ccc} a & d & g\newline b & e & h\newline c & f & i \end{array} \right] $$

運算出$x,y,\eta$:

$$ \begin{split} x &= (c+2f+i)-(a+2d+g)\newline y &= (g+2h+i)-(a+2b+c)\newline \eta &= \sqrt{x^{2}+y^{2}} \end{split} $$

考慮這個 $\eta$ 和臨界值 $\epsilon$ 的關係:

$$ \begin{split} e&=0,&\eta\geq\epsilon\newline e&=255,&\eta<\epsilon \end{split} $$

fin_2d_arr = np.zeros((height-1, width-1))

for k in range(1, (height-1), 1):
​    for j in range(1, (width-1), 1):
​        # cal a, b, c, d, e, f, g, h, i
​        # -------
​        # |a|d|g|
​        # -------
​        # |b|e|h|
​        # -------
​        # |c|f|i|
​        # -------
​        a = gray_2d_arr[k-1][j-1]
​        b = gray_2d_arr[k][j-1]
​        c = gray_2d_arr[k+1][j-1]
​        d = gray_2d_arr[k-1][j]
​        e = gray_2d_arr[k][j]
​        f = gray_2d_arr[k+1][j]      
​        g = gray_2d_arr[k-1][j+1]
​        h = gray_2d_arr[k][j+1]
​        i = gray_2d_arr[k-1][j+1]

        # cal x, y, value
        # x = (c+2f+i)-(a+2d+g)
        # y = (g+2h+i)-(a+2b+c)
        # value = sqrt(x^2+y^2)
        x = (c + 2*f + i) - (a + 2*d + g)
        y = (g + 2*h + i) - (a + 2*b + c)
        value = (x*x+y*y)**0.5
                 
        if (value >= epsilon):
            fin_2d_arr[k-1][j-1] = 0
        elif (value < epsilon):
            fin_2d_arr[k-1][j-1] = 255

輸出邊框圖片

plt.subplot(121), plt.imshow(gray_2d_arr, cmap = "gray"), plt.title("Original Image"), plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(fin_2d_arr, cmap = "gray"), plt.title("Edge Image"), plt.xticks([]), plt.yticks([])
plt.show() # 如果不要在 terminal 跳出圖就註解掉這行

輸出外框圖片的檔案

fin_arr = fin_2d_arr.flatten()

outputfilename = filename[:-4] + str("_out.pgm")

outputfile = open(outputfilename, "w") 
outputfile.write("P2")
outputfile.write("\n")
outputfile.write(str(width - 1) + str(" ") + str(height - 1))
outputfile.write("\n")
outputfile.write(str(255))
outputfile.write("\n")
for i in fin_arr:
    outputfile.write(str(int(i)))
    outputfile.write(str(" "))

結果