Saturday, December 1, 2012

การ crop และ paste รูป

ในบทความนี้ เราจะลองใช้หลักการของการตัดและแปะ เพื่อสร้าง effect แบบง่ายๆให้กับรูป คือการเคลื่อนรูปไปบางส่วน ให้ได้ผลลัพธ์ดังนี้



โดยกำหนดว่าให้มีการเคลื่อนของรูปไปประมาณ 30% ของความกว้างของรูป

ก่อนอื่น ต้องเพิ่มเมนูเช่น
processMenu.add_command(label="Roll", command=self.imgRoll)

แล้วก็สร้าง method คือ imgRoll ดังนี้
def imgRoll(self):
    #assume rolling horizontally by 30% of width
    w,h = self.im.size
    roll = int(0.30*w)
    #crop 2 parts
    part1 = self.im.crop((0,0,roll,h)) #(left, upper, right, lower)
    part2 = self.im.crop((roll,0,w,h))
    #create new image
    imr = Image.new("RGB",(w,h))
    #swap and paste crop images
    imr.paste(part2,(0,0,w-roll,h))
    imr.paste(part1,(w-roll,0,w,h))
    #set image to rolled image
    self.im = imr.copy()
    self.showImage()

การประมวลผลแต่ละพิกเซลของรูป

จากบทความที่แล้ว เราสามารถเข้าถึงพิกเซลของรูปและแก้ไขได้ ซึ่งเป็นกระบวนการที่สำคัญและสามารถประมวลผลภาพแบบซับซ้อนได้ อย่างไรก็ตาม หากเราต้องการเฉพาะการประมวลผลภาพแต่ละพิกเซล (Point Operation) ก็สามารถใช้คำสั่งที่อาจง่ายขึ้นได้ ได้แก่

out = im.point(function)

ตัวอย่างเช่น หากเราต้องการปรับค่า contrast ของรูป ซึ่งทำได้โดยการคูณแต่ละพิกเซลด้วยค่าคงที่ค่าหนึ่ง เช่น 1.5 ก็จะใช้คำสั่ง

out = im.point(lambda i: i*1.5)

(หมายเหตุ lambda เป็นเทคนิคการเขียนฟังก์ชันเฉพาะกิจของ python เรียกว่า anonymous functions เพื่อที่เราจะไม่ต้องเขียนฟังก์ชันใหม่แยกออกมา)

ตัวอย่างผลลัพธ์ที่เราต้องการ





โค้ดของโปรแกรมก็เพิ่มเติมจากเดิมดังนี้
เมนู
        processMenu.add_command(label="Contrast",command=self.imgContrast)

method
    def imgContrast(self):
        self.im = self.im.point(lambda i: i*1.5)
        self.showImage()

หมายเหตุ
1. คำสั่ง Image.point มีผู้ระบุไว้ว่าเร็วกว่าคำสั่ง Image.load ในการประมวลผลพิกเซล หากมีโอกาสจะได้ทดลองวัดความเร็วเปรียบเทียบกันครับ
2. หากต้องการแยกเป็นฟังก์ชันออกมาต่างหาก ก็ทำได้โดย


    def myContrast(self,i):
        return i*1.5

    def imgContrast(self):
        self.im = self.im.point(self.myContrast)
        self.showImage()

การเข้าถึงพิกเซลของรูป

แม้ว่า Python Imaging Library (PIL) จะมีคำสั่งในการประมวลผลภาพหลากหลาย แต่ในบางครั้งเราอาจจะต้องการที่จะประมวลผลแต่ละพิกเซลเอง ก็จะจำเป็นที่จะต้องทราบการอ่านและแก้ไขพิกเซลนั้น

การอ่านพิกเซล จะทำได้โดย
1. อ่านพิกเซลของรูปทั้งหมด แล้วนำไปเก็บไว้ในโครงสร้างข้อมูลที่เรียกว่า pixel access object ที่มีลักษณะคล้ายกับตัวแปรชุดสองมิติ (2D array) โดยใช้คำสั่ง load ดังนี้
pix = im.load()
2. สามารถอ่านค่าของแต่ละพิกเซล โดยใช้คำสั่ง
pix[x,y]
เมื่อ x และ y เป็นพิกัด เริ่มต้นที่มุมซ้ายบน (0,0)

การแก้ไขค่าพิกเซล หลังจากอ่านค่าพิกเซลทั้งหมดแล้ว ทำได้โดย
pix[x,y] = value
ถ้าเป็นรูป grayscale ค่า value จะเป็นเลขโดดๆ ตั้งแต่ 0 ถึง 255
ถ้าเป็นรูป RGB ค่า value จะเป็นเลขชุด เช่น (100,100,100)

ลองดูการเพิ่มเมนูเพื่อทำการกลับสีรูป (inverse) เพื่อให้ได้ผลลัพธ์ดังนี้



โค้ดเฉพาะส่วนของฟังก์ชันนี้ คือ
def imgInverse(self):
    #load an image reference to a special structure like a list
    pix = self.im.load()
    #print pix[0,0]
    #get image size
    w,h = self.im.size
    #if grayscale image
    if self.im.mode=="L":
        #loop for each row and column
        for r in range(h):
            for c in range(w):
                pix[c,r] = 255 - pix[c,r]
                #Note that we can use getpixel(x,y) and putpixel(x,y)
                #but this method is faster
    elif self.im.mode=="RGB":
    #loop for each row and column
        for r in range(h):
            for c in range(w):
                p = pix[c,r]
                pix[c,r] = (255-p[0],255-p[1],255-p[2])
    self.showImage()

ลองเอาไปเชื่อมต่อกับโค้ดก่อนหน้านี้ได้ครับ
อ้างอิง
http://effbot.org/zone/pil-pixel-access.htm