002.01 圖片去外框處理

Jason990420發表於2019-08-26

主題: 002.01 圖片去外框處理

建檔日期: 2019/08/26
更新日期: None
語言: Python 3.7.4, PIL Pillow 6.1.0., tkinter 8.6
系統: Win10 Ver. 10.0.17763 繁體中文版(TW)

002.01 圖片去外框處理

程式庫的熟練還是要多寫程式, 才會瞭解一些相的使用細節, 下面是有關圖片去外框的處理, 內容不想作的太複雜, 所以有些地方, 並沒有列入考慮.

說明:

1. 去外框的方式是在圖片上, 以滑鼠左鍵點選要去掉的部份的一個點, 尋找附近在顏色誤差範圍內的點, 都改為白色, 可以重複點選處理, 只能回到上一個圖片.

2. 圖檔案的檔名在程式中, 沒作檔案選擇的功能, 也沒加入存檔的功能

3. 圖檔案尺寸太大可能在顯示會超出螢幕, 不在考慮中.

4. 因為使用遞迴呼叫, 圖片太大或太複雜可能會超出遞迴呼叫次數的要求, 僅設為2000次, 有必要可以再加大到數萬次.

5. 因為圖片中的邊緣是以誤差值來界定, 所以簡單的圖片可以設大的誤差值, 一次作到要求, 對於複雜的圖片, 只能一點一點來作.

結果

1. 簡單的圖(顏色簡單, 本體與外圍顏色差距大, 通常一次點選就能完成)

002.01 圖片去外框處理
002.01 圖片去外框處理

2. 複雜的圖(指的是本體的顏色色與外圍的顏色過於接近, 而且有太多不同的顏色成份, 需經過多次的點選處理)

002.01 圖片去外框處理

程式

from tkinter import *
from PIL import Image, ImageTk

class display():
    # Canvas的圖片顯示
    def __init__(self, widget, imageObj):
        self.center = (int(imageObj.width/2), int(imageObj.height/2))
        self.photo = ImageTk.PhotoImage(image=imageObj)
        self.image = widget.create_image(self.center, image=self.photo)

def check(x0, y0):
    # 確認圖點顏色是不要轉換成白點
    r, g, b = im.getpixel((x0,y0))
    checked[x0][y0] = True
    if limit[0][0] <= r <= limit[0][1]:
        if limit[1][0] <= g <= limit[1][1]:
            if limit[2][0] <= b <= limit[2][1]:
                return True
    return False

def check_point(x0, y0, new_points):
    # 檢查圖點要不要處理
    # 超出圖面, 已在要處理清單中, 顏色不對, 或已處理的點, 不處理
    if 0<=x0<im.width and 0<=y0<im.height:
        if (x0, y0) not in new_points:
            if not checked[x0][y0]:
                if check(x0,y0):
                    new_points.append((x0, y0))

def fill(points):
    # 處理清單中的點, 還有上下左右四點是否加清單中待處理
    global checked, count
    count += 1
    print('Recursion count :', count)

    if points==[]:
        return

    new_points = []
    for point in points:
        x = point[0]
        y = point[1]
        checked[x][y] = True

        if check(x, y):
            im_new.putpixel((x,y), (255,255,255))
            check_point(x-1, y, new_points)
            check_point(x+1, y, new_points)
            check_point(x, y-1, new_points)
            check_point(x, y+1, new_points)

    # 待處理清單, 遞迴再處理
    points = new_points[:]
    fill(points)

def process(event):
    global old_x, old_y, click_again, limit, trigger, points, im_new, checked, image01, im_old, im, error, count

    # 處理中, 不再處理新事件
    if not trigger:
        return
    trigger = False

    # 超出圖片範圍, 改為在圖片範圍
    if event.x >= im.width:
        x = im.width - 1
    else:
        x = event.x
    if event.y >= im.height:
        y = im.height - 1
    else:
        y = event.y

    # 取得圖點顏色, 並設定誤差範圍
    r, g, b = im.getpixel((event.x, event.y))
    limit = [(r-error, r+error), (g-error, g+error), (b-10, b+error)]

    # 設定起點為滑鼠左鍵點選位置
    points = [(event.x, event.y)]

    # 所有的點都未處理
    checked = [[False for i in range(im_new.height)] for j in range(im_new.width)]

    # 遞迴次數計算初值
    count = 0

    # 處理圖片, 並顯示
    fill(points)
    image01 = display(can01, im_new)

    # 只保留前一張原圖
    im_old = im.copy()
    im = im_new.copy()

    # 可再接受新的點選事件
    trigger = True

def change_error():
    # 輸入誤差值事件
    global error
    error = int(input('give the error value'))

def undo():
    # Undo 回前一張圖片, 只能Undo一次
    global im_new, im_old, image01, can01
    im_new = im_old.copy()
    image01 = display(can01, im_new)

trigger = True
error = 30  # 誤差初值
font = 'times 20 bold'
filename = '5.jpg' # 圖片檔名

im = Image.open(filename)

root = Tk() # 兩個Canvas顯示原圖及新圖+兩個Button功能為設誤差值及Undo

can00 = Canvas(root, bg='green', width=im.width, height=im.height)
can01 = Canvas(root, bg='green', width=im.width, height=im.height)
but10 = Button(root, bg='green', text = 'Set error value', font=font, fg='white', command=change_error)
but11 = Button(root, bg='green', text = 'Undo just one time', font=font, fg='white', command=undo)

can00.grid(row=0, column=0)
can01.grid(row=0, column=1)
but10.grid(row=1, column=0)
but11.grid(row=1, column=1)

# 兩個畫布都可以接受點選
can00.bind('<Button-1>', process)
can01.bind('<Button-1>', process)

im_old = im.copy()
im_new = im.copy()
image00 = display(can00, im)

# 遞迴最大次數
sys.setrecursionlimit(2000)

root.mainloop()

Jason Yang

相關文章