บทความนี้กล่าวถึงการใช้คลาส image กับโมดูลแสดงผล TFT-LCD ของบอร์ด Sipeed M1W dock suit ผ่านทางคลาส lcd (MaixPy’s lcd class) ดังภาพที่ 1 ที่มีมากับ MaixPy เพื่อศึกษารายการคำสั่งที่คลาส image เตรียมไว้ให้ และตัวอย่างโปรแกรมการใช้งานคำสั่งเกี่ยวกับการสร้างวัตถุบัฟเฟอร์ การล้างค่าในบัฟเฟอร์ การลบบัฟเฟอร์ การวาดเส้นตรง วงกลม สี่เเหลี่ยม แสดงตัวอักษร การบันทึกข้อมูลจากบัฟเฟอร์ลงการ์ดหน่วยความจำ (microSD Card) การค้นหาเส้นตรงในบัฟเฟอร์ (find_lines) การค้นหาวงกลมในบัฟเฟอร์ (find_circles) และการค้นหาสี่เหลี่ยม (find_rects) ในบัฟเฟอร์ด้วยฟังก์ชันทำงานที่มีมาให้ ซึ่งใช้หลักการของ Hough Transform เพื่อหาตำแหน่งและพารามิเตอร์ของวัตถุที่ค้นหา
image class
คลาส image เป็นคลาสสำหรับสร้างบัฟเฟอร์ของหน่วยความจำสำหรับเก็บข้อมูลภาพ และสามารถนำไปแสดงผลที่โมดูล lcd ด้วยคำสั่ง lcd.display( ) รายการคำสั่งที่ควรรู้จักและใช้งานได้ มีดังต่อไปนี้
คำสั่งเกี่ยวกับบัฟเฟอร์
Image()
คำสั่งสำหรับสร้างบัฟเฟอร์มี 2 รูปแบบ คือ สร้างเป้นบัฟเฟอร์ว่างเปล่า หรือสร้างจากภาพที่ต้องการ ดังนี้
บัฟเฟอร์ = image.Image()
บัฟเฟอร์ = image.Image( ชื่อภาพ )
ตัวอย่างการสร้างวัตถุบัฟเฟอร์ภาพเขียนได้ดังนี้
a = image.Image()
เมื่อสั่งแสดงผล a ด้วยคำสั่ง print(a) แล้วได้ผลลัพธ์ดังนี้
{"w":320, "h":240, "type"="rgb565", "size":153600}
จากผลลัพธ์นี้จะพบว่า บัฟเฟอร์ที่สร้างนั้นจะมีความกว้าง 320 จุด สูง 240 จุด แต่ละจุดเป็นข้อมูลแบบ rgb565 และบัฟเฟอร์นี้ใช้หน่วยความจำไป 153600 ไบต์ ดังนั้น เราแนะนำว่าในการใช้งานเมื่อบัฟเฟอร์ใดไม่ต้องการใช้หรือไม่ถูกใช้เป็นเวลานาน ควรที่จะนำออกจากหน่วยความจำด้วยตัวอย่างของโปรแกรมต่อไปนี้
>>> import gc, image
>>> gc.enable()
>>> gc.collect()
>>> print(gc.mem_free())
513184
>>> img = image.Image()
>>> print(gc.mem_free())
356928
>>> img = None
>>> print(gc.mem_free())
356640
>>> gc.collect()
>>> print(gc.mem_free())
512864
จากโค้ดด้านบนจะพบว่า ตอนแรกเริ่มนั้นหน่วยความจำมีอยู่ 513,184 ไบต์ เมื่อสร้างตัวแปรบัฟเฟอร์ชื่อ img ทำให้เหลือ 356,928 แต่เมื่อกำหนดให้บัฟเฟอร์เป็น None เมื่อสั่งตรวจสอบปริมาณหน่วยความจำยังคงเป็น 356,640 จึงได้ทดลองสั่ง gc.collect() อีกครั้งจึงทำให้หน่วยความจำเหลือ 512,864 ดังนั้น เมื่อกำหนดให้ตัวแปรบัฟเฟอร์เป็น None ต้องสั่ง gc.colelct() เพื่อให้ระบบบริหารจัดการหน่วยความจำใหม่อีกครั้งด้วย ซึ่งการทำแบบนี้ทำให้โปรแกรมทำงานช้าลงแต่แลกกับการบริหารหน่วยความจำที่ดีขึ้น
cut()
คำสั่ง cut() ใช้สำหรับตัดส่วนของบัฟเฟอร์ในกรอบที่กำหนด โดยรูปแบบของคำสั่ง เป็นดังนี้
บัฟเฟอร์ = วัตถุ.cut( x, y, w, h )
ตัวอย่างการแบ่งข้อมูลภาพออกเป็น 2 ส่วน เขียนดังนี้
import image
img = image.Image()
upImg = img.cut(0,0,img.width(),img.height()/2)
dwImg = img.cur(0,img.height()/2,img.width(),img.height()/2)
save()
กรณีที่ต้องการบันทึกบัฟเฟอร์ลงไฟล์สามารถสั่งงานได้ตามรูปแบบต่อไปนี้
วัตถุบัฟเฟอร์.save( “/sd/ชื่อไฟล์.jpg”)
width()
คำสั่ง width() คืนค่าความกว้างของบัฟเฟอร์
height()
คำสั่ง height() ทำหน้าที่คืนค่าความสูงของบัฟเฟอร์
format()
คำสั่ง format() ใช้สำหรับคืนค่ารูปแบบของข้อมูลที่เก็บในบัฟเฟอร์ ซึ่งมีดังนี้
- sensor.GRAYSCALE
- sensor.RGB565
- sensor.BAYER
- sensor.JPEG
size()
คำสั่ง size() เป็นคำสั่งรายงานจำนวนไบต์ของหน่วยความจำที่ถูกใช้เพื่อเก็บข้อมูลบัฟเฟอร์
get_pixel()
คำสั่ง get_pixel() ใช้สำหรับอ่านค่าสี R,G,B จากตำแหน่ง (x,y) ที่ระบุ โดบรูปแบบของการใช้งานเป็นดังนี้
(r, g, b) = วัตถุบัฟเฟอร์.get_pixel( x, y )
set_pixel()
กรณีที่ต้องการเปลี่ยนค่าพิกเซลที่ตำแหน่ง (x,y) ให้เป็นค่าสี (r,g,b) สามารถใช้คำสั่ง set_pixel() ตามรูปแบบการใช้งานดังนี้
วัตถุบัฟเฟอร์.set_pixel( x, y, (r,g,b) )
คำสั่งวาด
draw_string()
คำสั่งแสดงข้อความหรือวาดข้อความ ณ ตำแหน่ง (x,y) ด้วยข้อความ str โดยกำหนดค่าสีและขนาดของตัวอักษร มีรูปแบบของการใช้งานดังนี้
วัตถุบัฟเฟอร์.draw_string( x, y, str, (r,g,b), ขนาดตัวอักษร0
draw_line()
คำสั่งสำหรับวาดเส้นตรงในบัฟเฟอร์มีรูปแบบของการวาดจาก (x1,y1) ไป (x2,y2) ดังนี้
วัตถุบัฟเฟอร์.draw_line( x1, y1, x2, y2 )
หรือ
วัตถุบัฟเฟอร์.draw_line( x1, y1, x2, y2, (r,g,b) )หรือ
วัตถุบัฟเฟอร์.draw_line( x1, y1, x2, y2, (r,g,b), ความหนาของเส้น )
draw_circle()
คำสั่งสำหรับวาดวงกลมที่จุดศูนย์กลางอยู่ที่ (x,y) มีรัศมี r สั่งงานได้ดังนี้ โดยในรูปแบบที่ 4 สามารถกำหนดสถานะการเติมสีเป็น True เพื่อเติมสีลงในวงกลม แต่ถ้าเป็น False จะเป็นวงกลมที่มีแต่เส้นขอบ
วัตถุบัฟเฟอร์.draw_circle( x, y, r)
หรือ
วัตถุบัฟเฟอร์.draw_circle(x,y,r,(rr,gg,bb))หรือ
วัตถุบัฟเฟอร์.draw_circle(x,y,r,(rr,gg,bb),ความหนา)หรือ
วัตถุบัฟเฟอร์.draw_circle(x,y,r,(rr,gg,bb),ความหนา, สถานะการเติมสี)
draw_rectangle()
คำสั่งสำหรับวาดสี่เหลี่ยมขนาด w,h โดยมุมซ้ายของสี่เหลี่ยมอยู่ที่จำแหน่ง (x,y) มีรูปแบบของการใช้งานดังนี้
วัตถุบัฟเฟอร์.draw_rectangle( x, y, w, h )
หรือ
วัตถุบัฟเฟอร์.draw_rectangle( x, y, w, h, (rr, gg, bb) )หรือ
วัตถุบัฟเฟอร์.draw_rectangle( x, y, w, h, (rr, gg, bb), ความหนา )หรือ
วัตถุบัฟเฟอร์.draw_rectangle( x, y, w, h, (rr, gg, bb) , ความหนา, สถานะการเติมสี)
clear()
คำสั่งล้างค่าที่เก็บในบัฟเฟอร์มีรูปแบบการใช้ดังต่อไปนี้
วัตถุบัฟเฟอร์.clear()
draw_cross()
คำสั่งสำหรับวาดเครื่องหมายกากบาทที่กึ่งกลางอยู่ที่ตำแหน่ง (x,y) ขนาด n มีรูปแบบการใช้งานดังนี้
วัตถุบัฟเฟอร์.draw_cross( x, y, n, (r,g,b), ความหนา=1)
คำสั่งค้นหา
คำสั่งในกลุ่มค้นนี้ใช้ขั้นตอนวิธีที่เรียก Hough Transform ในการค้นหาเส้นตรง วงกลม หรือสี่เหลี่ยม ซึ่งผลลัพธ์ที่ได้นั้นอาจจะมีมากกว่าหรือน้อยกว่าของวัตถุจริง โดยผู้ใช้จำต้องกำหนดพารามิเตอร์การทำงานแบบละเอียดเพื่อให้ได้ผลลัพธ์ที่แม่นยำขึ้น
find_lines()
รูปแบบของคำสั่งค้นหาเส้นตรงในวัตถุบัฟเฟอร์ด้วยคำสั่ง find_lines() และส่งคืนกลับเป็นลิสต์ของข้อมูล เป็นดังนี้
รายการที่พบ = วัตถุบัฟเฟอร์.find_lines()
หรือ
รายการที่พบ = วัตถุบัฟเฟอร์.find_lines((x,y,w,h))
ลิสต์ที่ได้กลับมามีจำนวนเท่ากับจำนวนเส้นตรงที่พบ โดยแต่ละเส้นที่พบนั้นมีข้อมูลดังนี้
- x1 ค่า x เริ่มต้น
- y1 ค่า y เริ่มต้น
- x2 ค่า x สิ้นสุด
- y2 ค่า y สิ้นสุด
- length ความยาวของเส้นตรง
- magnitude เป็นตัวบอกความคล้ายของวัตถุที่ค้นหา ยิ่งมีค่ามากยิ่งดี
- theta ค่ามุม
- rho
find_circles()
กรณีที่ต้องการค้นหาวัตถุประเภทวงกลมในบัฟเฟอร์สามารถกระทำได้โดยใช้งานคำสั่ง find_circules() ตามรูปแบบของคำสั่งต่อไปนี้
รายการที่พบ = วัตถุบัฟเฟอร์.find_circles( )
หรือ
รายการที่พบ = วัตถุบัฟเฟอร์.find_circlrs( (x, y, w, h) )
ผลลัพธ์จากการค้นหาวงกลมจะคินกลับมาในรูปแบบของลิสต์เช่นเดียวกันกับการค้นหาเส้นตรง แต่รายการข้อมูลในลิสต์มีความแตกต่างกัน คือ รายการที่ได้ประกอบด้วย จุดกึ่งกลาง (x,y) รัศมี และค่า magnitude ดังภาพที่ 5
find_rects()
การค้นหาสี่เหลี่ยมที่อยู่ในบัฟเฟอร์ด้วยคำสั่ง find_rects() มีรูปแบบของการใช้งานดังนี้
รายการที่พบ = วัตถุบัฟเฟอร์.find_rects()
หรือ
รายการที่พบ = วัตถุบัฟเฟอร์.find_rects((x,y,w,h))
โดยผลลัพธ์จกาการค้นหานั้นอยู่ในรูปแบบของตัวแปรลิสต์ของรายการต่อไปนี้
- x ค่าแกน x ของมุมซ้ายบน
- y ค่าแกน y ของมุมซ้ายบน
- w ค่าความกว้าง
- h ค่าความสูง
- magnitude เป็นตัวบอกความคล้ายของวัตถุที่ค้นหา ยิ่งมีค่ามากยิ่งดี
ตัวอย่างโปรแกรม
ตัวอย่างโปรแกรม img1
ตัวอย่างที่ 1 เป็นการวาดหน้าจอด้วย draw_string() และแสดงอัตราการแสดงภาพต่อ 1 วินาที หรือ fps (frame per second) โดยหลักการของการวัดอัตราการอัพเดตหน้าจอที่เป็นหน่วยจำนวนภาพต่อวินาทีต้องนำเข้าคลาส time และเรียกใช้คำสั่งต่อไปนี้
- clock() สำหรับอ่านค่าเวลา
- tick() ในทำการนับ
- fps() รายงานจำนวนภาพที่แสดงผลใน 1 วินาที
โค้ดโปรแกรมของการแสดงข้อความและอัตราการแสดงผลเป็นดังนี้ โดยตัวอย่างผลลัพธ์เป็นดังภาพที่ 2
# img1 - By: JarutEx - Mon Oct 4 2021
import lcd, image, time
lcd.freq(80000000)
lcd.init(color=(255,0,0))
clock = time.clock()
img = image.Image()
while(True):
clock.tick()
img.clear()
img.draw_string(10,100,"JarutEx",(232,232,18),7)
img.draw_string(10,20,"fps={}".format(clock.fps()),(252,252,252),2)
lcd.display(img)
ตัวอย่างโปรแกรม img2
ในตัวอย่างโปรแกรม img2 เป็นการวาดเส้นตรงลงในบัฟเฟอร์ หลังจากนั้นทำการค้นหาเส้นตรงในนบัฟเฟอร์ ซึ่งผลลัพธ์ของการค้นหานั้นเป็นดังภาพที่ 3
# img2 - By: JarutEx - Mon Oct 4 2021
import lcd, image, time, gc, machine
gc.enable()
gc.collect()
lcd.freq(80000000)
lcd.init(color=(255,0,0))
img = image.Image()
fLines = None
gc.collect()
img.clear()
img.draw_line( 10, 10, 200, 100, coloe=(255,255,255))
fLines = img.find_lines((0,0,img.width(),img.height()))
nLines = len(fLines)
if (nLines > 0):
img.draw_string(10,10,"lines={}".format(nLines),(8,242,232),2)
for i in range(nLines):
print("{}".format(fLines[i]))
lcd.display(img)
time.sleep(2)
fLines = None
img = None
lcd.deinit()
gc.collect()
machine.reset()
ตัวอย่างโปรแกรม img3
ตัวอย่างโปรแกรม img3 เเพิ่มความซับซ้อนของโปรแกรมด้วยการสุ่มจำนวนวงกลม ตำแหน่งจุดศูนย์กลางของวงกลม และรัศมีของวงกลมและวาดลงบัฟเฟอร์ ซึ่งได้ตัวอย่างของการวาดเป็นดังภาพที่ 4 หลังจากนั้นทำการค้นหาวงกลมในบัฟเฟอร์ได้ตัวอย่างผลลัพธ์ของการค้นหาเป็นดังภาพที่ 5
# img3 - By: JarutEx - Mon Oct 4 2021
import lcd, image, time, gc, machine
import random
gc.enable()
gc.collect()
lcd.freq(80000000)
lcd.init(color=(255,0,0))
img = image.Image()
fLines = None
gc.collect()
img.clear()
for i in range(1+random.getrandbits(4)):
img.draw_circle( random.getrandbits(7)+20, random.getrandbits(7)+20,
random.getrandbits(4)+4, (255,255,255), 1)
fCircles = img.find_circles((0,0,img.width(),img.height()))
nCircles = len(fCircles)
if (nCircles > 0):
for i in range(nCircles):
print("{}".format(fCircles[i]))
lcd.display(img)
time.sleep(5)
fCircles = None
img = None
lcd.deinit()
gc.collect()
machine.reset()
img.draw_circle(x,y,r,(rr,gg,bb),2,(rr,gg,bb))
ตัวอย่างโปรแกรม img4
ตัวอย่างโปรแกรมนี้เป็นการวาดสี่เหลี่ยมลงในบัฟเฟอร์ โดยเพิ่มเรื่องของการสุ่มสี ความหนา และกำหนดให้เป็นสี่เหลี่ยมแบบลงสี โดยตัวอย่างผลลัพธ์ของการสุ่มสี่เหลี่ยมได้ผลดังภาพที่ 6 และตัวอย่างผลลัพธ์จากการค้นหาสี่เหลี่ยมเป็นตามภาพที่ 7
# img4 - By: JarutEx - Mon Oct 4 2021
import lcd, image, time, gc, machine
import random
gc.enable()
gc.collect()
lcd.freq(80000000)
lcd.init(color=(255,0,0))
img = image.Image()
fLines = None
gc.collect()
img.clear()
for i in range(1+random.getrandbits(4)):
img.draw_rectangle(
random.getrandbits(8),random.getrandbits(8),
4+random.getrandbits(6),4+random.getrandbits(6),
(random.getrandbits(8),random.getrandbits(8),random.getrandbits(8)),
random.getrandbits(2)+1,
True)
fRects = img.find_rects((0,0,img.width(),img.height()))
nRects = len(fRects)
if (nRects > 0):
for i in range(nRects):
print("{}".format(fRects[i]))
lcd.display(img)
time.sleep(5)
fRects = None
img = None
lcd.deinit()
gc.collect()
machine.reset()
สรุป
จากบทความนี้จะพบว่าบัฟเฟอร์หรือ image ที่สร้างขึ้นมานี้เป็นส่วนสำคัญในการประมวลผลภาพ เนื่องจาก เมื่ออ่านข้อมูลภาพจากเซ็นเซอร์ ข้อมูลที่อ่านมาเหล่านั้นล้วนถูกจัดเก็บลงบัฟเฟอร์ ด้วยเหตุนี้ เมื่อเรามีบัฟเฟอร์ และเราสามารถวาดทับลงบัฟเฟอร์ได้ เราจึงสามารถแสดงข้อมูลซ้อนลบในบัฟเฟอร์ภาพที่อ่านมาได้ นอกจากนี้ ในชุดคำสั่งต้นหาข้อมูลเส้นตรง หรือวงกลมในบัฟเฟอร์ ทำให้เราสามารถค้นหาสิ่งที่ดูคล้ายวงกลมหรือเส้นตรงจากในภาพได้ เมื่อเราได้ข้อมูลที่ค้นหาจากบัฟเฟอร์เป็นที่เรียบร้อย ก็สามารถคัดแต่ละราการมาพิตารณาต่อว่าสิ่งนั้นมีความคล้ายมากน้อยเท่าใด หรือสิ่งที่อยู่ในส่วนนั้นเป็นสิ่งใด และสุดท้ายนี้ ขอให้สนุกกับการเขียนโปรแกรมครับ
ท่านใดต้องการพูดคุยสามารถคอมเมนท์ได้เลยครับ
แหล่งอ้างอิง
- Wikipedia : Hough transform
- OpenMV : MicroPython documentation
- OpenMV : Class image : image object
(C) 2020-2021, โดย อ.ดนัย เจษฎาฐิติกุล/อ.จารุต บุศราทิจ
ปรับปรุงเมื่อ 2021-10-05, 2021-12-19