ComBioLaw.De » Blog » เขียนโปรแกรม » Play PIL (Python Image Library) with NumPy
Play PIL (Python Image Library) with NumPy
PIL ถือเป็นโมดูลสำหรับ Image Processing ที่ทรงพลัง ใช้งานง่าย และสนับสนุนไฟล์ภาพหลายรูปแบบด้วยกัน (ดูฟอร์แมตภาพที่ PIL สนับสนุนได้ที่ PIL Handbook) ความสามารถของ PIL ครอบคลุมงานเกี่ยวกับ Image Processing ที่ใช้งานปกติได้เกือบทั้งหมด แต่ทั้งนี้ทั้งนั้นยังมีงานบางประเภทที่เราต้องออกแรงเขียนโปรแกรมเอง ตัวอย่างการเขียนโปรแกรมด้วย PIL สามารถดูได้ที่ เอ็นโปรเทค ซึ่งจากตัวอย่างจะเห็นได้ว่า เราสามารถเขียนโปรแกรมร่วมกับ PIL ได้โดยใช้ Python List ด้วยวิธีการดังกล่าว เราต้องวนลูปหลาย ๆ รอบ เพื่อรับค่าแต่ละ Pixel จากรูปภาพ แล้วนำไปประมวลผลอีกครั้งหนึ่ง ผลที่ได้คือ การเขียนโปรแกรมที่ซับซ้อน และทำงานช้า ด้วยความที่ผมทำงานกับ NumPy ทุกวี่วัน ก็เลยอดไม่ได้ที่จะนำ PIL มาทำงานร่วมกับ NumPy เพื่อผนึกกำลังสองความสามารถให้เป็นหนึ่ง นำการคำนวนแบบ numerical ของ NumPy มาใช้ในงาน Image Processing ทำให้การคำนวน และประมวลผลต่าง ๆ ง่ายขึ้น มีประสิทธิภาพมากขึ้น และสามารถนำฟังก์ชั่นทางคณิตศาสตร์อื่น ๆ ของ NumPy และ SciPy มาใช้งานร่วมกับ PIL ได้ การแปลงรูปภาพให้อยู่ในรูปของอะเรย์สำหรับ NumPy นั้น สามารถทำได้ง่าย ๆ ด้วยคำสั่ง ... |
|
|
import Image, numpy # Read image from file im = Image.open('image.png') # Covert image to array im_array = numpy.asarray(im)plain code ผลที่ได้คืออะเรย์ 3 มิติ ขนาดเท่ากับ "ความยาวของรูปภาพ x ความกว้างของรูปภาพ x 3" ค่า x 3 คือค่า RGB หรือค่าสีแดง สีเขียว และสีน้ำเงินของรูปภาพซึ่งมีค่าระหว่าง 0-255 เราสามารถนำอะเรย์ดังกล่าวมาคำนวนแบบ numerical ได้ทันที เพื่อให้เห็นภาพผมขอยกตัวอย่าง การเปลี่ยนภาพสีในระบบ RGB ให้เป็นภาพแบบ negative หรือภาพที่มีสีแบบฟิล์มเนกาทีฟ ที่เราใช้ถ่ายรูป (มีใครยังใช้กล้องฟีล์มถ่ายรูป รายงานตัวด่วน)
from Image import open, fromarray from numpy import asarray # Read image from file im = open('child.jpg') # Convert image to array im_array = asarray(im) # Convert image to negative result = 255-im_array # Save result to file fromarray(result).save('child_negative.jpg')plain code จากตัวอย่างการคำนวนหาค่า negative ของรูป มีเพียงบรรทัดเดียวเท่านั้นคือ result = 255-im_array ที่เหลือเป็นการอ่านไฟล์และเซพไฟล์ภาพ ข้อควรระวังคือ im_array ที่ได้จากการเปลี่ยนข้อมูลจากรูปภาพเป็นอะเรย์ เป็นอะเรย์แบบ read only คือ อ่านได้อย่างเดียวเปลี่ยนแปลงแก้ไขไม่ได้ ข้างล่างคือรูปที่ได้จากโปรแกรม
![]() ภาพเริ่มต้น
ภาพที่ได้จากการคำนวนแบบ negative
เมื่อเปรียบเทียบกับ Image Processing แบบไม่ใช้ NumPy ตามตัวอย่างข้างล่าง จะเห็นความทรงพลังของการคำนวนแบบ numerical และข้อแตกต่างในการเขียนโปรแกรมที่ชัดเจน (ผลลัพธ์ออกมาเหมือนกันทุกประการ)
from Image import open, fromarray from numpy import asarray # Read image from file im = open('child.jpg') # Convert image to negative pixel to pixel negat = lambda x : 255-x for i in range(im.size[0]) : for j in range(im.size[1]) : im.putpixel((i,j), tuple(map(negat, im.getpixel((i,j))))) # Save result to file im.save('child_negative2.jpg')plain code ตัวอย่างต่อไป เป็นการย่อขนาดภาพ ซึ่งใน PIL มีเมโธท resize ให้ใช้อยู่แล้ว แต่ตัวอย่างนี้ ต้องการแสดงให้เห็นถึงการเข้าถึงข้อมูลของอะเรย์ที่ได้จากภาพด้วย array slicing
import Image from numpy import * # Read image from file im = Image.open('child.jpg') # Resize image with PIL-method im.resize((im.size[0]/2, im.size[1]/2)).save('child_pil_resize.jpg') # Resize image with NumPy Image.fromarray(asarray(im)[::2,::2,:]).save('child_num_resize.jpg')plain code ในตัวอย่างการย่อขนาดภาพอยู่ที่ประโยค asarray(im)[::2,::2,:] ซึ่งมีความหมายว่า เปลี่ยนข้อมูลภาพให้เป็นอะเรย์ และเลือก ข้อมูล pixel เว้น pixel ทั้งในแนวตั้ง และแนวนอน และเลือก ทุกสี อาจจะงง ๆ แต่สามารถอ่านข้อมูลเกี่ยวกับ array slicing ได้ที่ Numericla Programming สำหรับผลการเปลี่ยนขนาดภาพเหมือนกันคือ
ตัวอย่างต่อไปเป็นการแปะภาพหนึ่งลงบนอีกภาพหนึ่ง ซึ่งใน PIL มีเมโธท paste ให้ใช้เหมือนกัน ทั้งวิธีแบบ numerical และ paste ของ PIL ไม่แตกต่างกันมากนัก ทั้งในเรื่องความยากง่ายในการเขียนโปรแกรม ความเร็วของโปรแกรม และผลที่ได้รับ แต่ในกรณีการแปะภาพที่เป็น transparency วิธีแบบ numerical ค่อนข้างง่ายกว่าวิธีที่ไม่ใช้การคำนวนแบบ numerical พอสมควร เพราะการแปะภาพที่เป็น transparency ต้องมีการสร้าง layer ขึ้นมาอีกหนึ่งชั้น จากนั้นก็แปะภาพที่มีขนาดเล็กกว่าลงบน layer ดังกล่าว แล้วนำภาพต้นฉบับมารวมกับ layer ด้วยฟังก์ชั่น composite
import Image from numpy import * # Read source child = asarray(Image.open('child.jpg')) text = asarray(Image.open('text.png')) # Find vertical position of text v_pos = child.shape[0] - text.shape[0] - 20 # Find horizontal position of text h_pos = (child.shape[1] - text.shape[1])/2 # Add text to image result = child.copy() result[v_pos:v_pos+text.shape[0], h_pos:h_pos+text.shape[1]] = text[:,:,:3] # Convert result to image and save to a file Image.fromarray(result).save('child_add_text.jpg') # Add transparency text to image # Read source text_trans = asarray(Image.open('text_transparent.png')) # Create transparency layer layer = zeros((child.shape[0],child.shape[1],4), dtype=int8) layer[v_pos:v_pos+text_trans.shape[0], h_pos:h_pos+text_trans.shape[1]] = text_trans layer = Image.fromarray(layer, 'RGBA') # Compose image with transparency text result = Image.composite(layer, Image.fromarray(child).convert('RGBA'), layer) # Save result to a file result.save('child_add_text_transparent.png', 'PNG')plain code
![]() ผลการแปะภาพแบบปกติ
ผลการแปะภาพแบบ transparency
จากการแปะภาพแบบ transparency จะเห็นได้ว่าภาพแบบ transparency ไม่ได้มีโหมดการทำงานแบบ RGB แต่เป็นโหมดการทำงานแบบ RBGA (Reg Green Blue Alpha) ขนาดของอะเรย์จึงเป็น "ความยาวของรูปภาพ x ความกว้างของรูปภาพ x 4" แทนที่จะเป็น "ความยาวของรูปภาพ x ความกว้างของรูปภาพ x 3" ค่า Alpha คือค่า transparency ค่า Alpha ยิ่งน้อยลงเท่าใด ภาพก็จะโปร่งใสมากขึ้นเท่านั้น และที่สำคัญ ไฟล์ภาพแบบ JPEG ไม่สามารถทำบนโหมด RGBA ได้ การใช้โหมด RBGA ควรใช้ไฟล์แบบ PNG แทน ตัวอย่างต่อมาไม่เกี่ยวกับการใช้ NumPy เท่าไร แต่เป็นการนำตัวอย่างข้างบนมาปรับใช้ในการใส่วันที่ลงไปในภาพ ซึ่งลำพังความสามารถของ PIL และ NumPy คงไม่เพียงพอ ต้องนำความสามารถของ Inkscape เข้ามาช่วยด้วย โดยการใช้ Inkscape ในการสร้างภาพ SVG เนื่องจากไฟล์ SVG เป็น textfile ที่ใช้อธิบาย Vector Graphic เราจึงสามารถใช้ Python แก้ไขเนื้อหาภายในภาพได้ โดยไม่ต้องใช้โมดูลอื่น ๆ เพิ่มเติม ในไฟล์ SVG ที่ถูกสร้างด้วย Inkscape จะมีประโยค 00.00.0000 เป็นเทมเพลทอยู่ เราสามารถแทนค่าประโยคดังกล่าวด้วยวันที่ปัจจุบัน จากนั้นก็บันทึกข้อมูลที่ได้ไว้ในไฟล์อีกไฟล์หนึ่ง ปัญหาคือว่า เราไม่สามารถทำงานร่วมกับไฟล์ SVG ด้วย PIL หรือ NumPy ได้ ตรงนี้เราต้องการ Inkscape อีกครั้งหนึ่ง ในการคอนเวิร์ทไฟล์ SVG เป็นไฟล์ PNG จากนั้นก็ใช้วิธีรวมไฟล์ transparent ในตัวอย่างก่อนหน้านี้ ในการรวมไฟล์ภาพเริ่มต้น กับไฟล์วันที่ที่ได้จาก Inksacpe
import Image import os from numpy import * from time import * # Generate date image # Read SVG template date_svg = open('date.svg').read() # Modify SVG image today = gmtime() if today[1] >= 10 : today_str = '%2d.%d.%d'%(today[2], today[1], today[0]) else : today_str = '%2d.0%d.%d'%(today[2], today[1], today[0]) date_svg = date_svg.replace('00.00.0000', today_str) # Geneate PNG image from SVG image open('today.svg','w').write(date_svg) os.system('inkscape today.svg -e today.png') # Add date to image # Read source child = asarray(Image.open('child.jpg')) today_png = asarray(Image.open('today.png')) # Find vertical position of text v_pos = child.shape[0] - today_png.shape[0] - 20 # Find horizontal position of text h_pos = (child.shape[1] - today_png.shape[1])/2 # Create transparency layer layer = zeros((child.shape[0],child.shape[1],4), dtype=int8) layer[v_pos:v_pos+today_png.shape[0], h_pos:h_pos+today_png.shape[1]] = today_png layer = Image.fromarray(layer, 'RGBA') # Compose image with transparency text result = Image.composite(layer, Image.fromarray(child).convert('RGBA'), layer) # Save result to a file result.save('child_add_today.png', 'PNG')plain code
![]() ภาพที่ได้จากการเพิ่มวันที่ในภาพ
ตัวอย่างสุดท้าย เป็นตัวอย่างที่นำทฤษฎีเกี่ยวกับ Digital Signal Processing มาใช้ใน Image Processing ด้วยการใช้ IIR-Filter กรองสัญญาณความถี่สูงในภาพออก (?) (สัญญาณความถี่สูงในภาพคือ การเปลี่ยนแปลงสีในรูปภาพอย่างรวดเร็ว ผลที่ได้จากการกรองสัญญาณความถี่สูงออกจากภาพ คือ Blur-Filter) ซึ่งในตัวอย่างนี้ความทรงพลังของการนำ PIL มาทำงานร่วมกับ NumPy ชัดเจนยิ่งขึ้น เพราะเป็นการเปิดประตูไปสู่โมดูลทางคณิตศาสตร์อื่น ๆ อีกมากมายใน SciPy
import Image from numpy import * from scipy.signal import * # Read source child = asarray(Image.open('child.jpg')) # Calcurate 2rd order IIR-Filter tabs b, a = bessel(2, 0.09*pi) # Filtering the image result = zeros_like(child) for i in range(3) :result[:,:,i] = lfilter(b, a, child[:,:,i]) # Save result to file Image.fromarray(result).save('chuild_filtered.jpg')plain code
![]() ภาพที่ได้จาก IIR-Filter
|
|
27 Jul 08 | by | tags เขียนโปรแกรม Python PIL NumPy Image Processing
nazt
numpy ทำแบบ ไม่วนลูป ได้ไหมครับ ตอนนี้มองไม่ค่อยออกเลยครับ
pixel = resized.load() manual_gray=Image.new('L',resized.size) # print mtuple for i in range(width): for j in range(height): weight=(pixel[i,j][0]+pixel[i,j][1]+pixel[i,j][2])/3 manual_gray.putpixel((i,j),(weight)) gray.show() |









house
มันเทพจริงๆนะ ผมเอามาทำ digital signature
เสียแต่ถ้าขาด numPy นี่มันช้าไปแบบเห็นชัดๆเลย(ที่เคยไปโพสต์ให้คุณโบว์ช่วยไง)
29 Jul 08