5.6. การวาดรูปทรงและข้อความ¶
อัลกอริทึมที่ตัดสินใจบางอย่างเกี่ยวกับภาพมักต้องการให้การตัดสินใจนั้นมองเห็นได้ ตัวตรวจจับ blob จะหาบริเวณที่แอปพลิเคชันสนใจ แอปพลิเคชันต้องการให้วาดบริเวณนั้นบนเฟรมเพื่อให้ผู้ควบคุม -- หรือนักพัฒนาที่รันสคริปต์ -- เห็นสิ่งที่พบ การแปลงพิกัดจะ map ตำแหน่ง input ไปยังตำแหน่ง output การ debug มักหมายถึงการทำเครื่องหมายตำแหน่งทั้งสองบนภาพเดียวกัน การแสดงตัวอย่างใน IDE จะอ่านสิ่งที่อยู่ใน frame buffer ในขณะที่มัน poll ดังนั้นวิธีที่ง่ายที่สุดในการทำให้ output ของอัลกอริทึมมองเห็นได้คือการเขียน annotations ลงใน frame buffer โดยตรง กลุ่มฟังก์ชัน drawing บนคลาส Image คือชุดเครื่องมือสำหรับงานนั้นโดยเฉพาะ
5.6.1. primitives¶
drawing method แต่ละตัวจะวางเครื่องหมายชนิดหนึ่งบนภาพ รายการนั้นมีขนาดเล็กและอยู่ใกล้กับ primitives เรขาคณิตที่ annotation จำเป็นต้องใช้จริง:
draw_line()-- ส่วนของเส้นตรงระหว่างจุดสิ้นสุดสองจุดdraw_rectangle()-- สี่เหลี่ยมที่จัดแนวตามแกน ทั้งแบบกลวงหรือทึบdraw_circle()-- วงกลมรอบจุดกึ่งกลาง ทั้งแบบกลวงหรือทึบdraw_ellipse()-- วงรีที่มีการหมุนได้ตามอิสระdraw_cross()-- เครื่องหมายบวกที่จุดหนึ่ง ซึ่งเป็นเครื่องหมายปกติสำหรับ centroid หรือ click targetdraw_arrow()-- ลูกศรจากจุดเริ่มต้นไปยังจุดสิ้นสุดdraw_edges()-- ด้านทั้งสี่ของรูปสี่เหลี่ยมด้านไม่เท่า กำหนดโดยจุดมุมทั้งสี่ วิธีธรรมชาติในการร่างขอบ tag ที่ตรวจจับได้หรือบริเวณที่ถูก warp ด้วย perspectivedraw_string()-- ข้อความจาก bitmap font ที่มีอยู่ภายใน
ทุก method เหล่านี้จะปรับเปลี่ยนภาพแหล่งที่มา in place และคืนค่าภาพเดิมสำหรับการ chain ตามรูปแบบ operating-method ที่กำหนดไว้ก่อนหน้า
primitives การวาดแปดแบบ หนึ่งแบบต่อแผง แต่ละ method สร้างเครื่องหมายชนิดหนึ่ง¶
5.6.2. สี¶
drawing method ทุกตัวรับ argument color ที่กำหนดว่าจะเขียนค่าอะไรลงในแต่ละพิกเซลที่ถูกทาสี รูปแบบของ argument นั้นขึ้นอยู่กับ format ของภาพ สำหรับภาพ RGB565 รูปแบบธรรมชาติคือ tuple (r, g, b) โดยแต่ละช่องอยู่ใน 0 -- 255 โมดูลจะบีบอัดลงเป็น RGB565 word 16 บิตก่อนเขียน สำหรับภาพ grayscale รูปแบบธรรมชาติคือ integer ความสว่างเดี่ยวตั้งแต่ 0 (ดำ) ถึง 255 (ขาว) method ยังยอมรับค่า raw ที่จัดเก็บของ format ด้วย ซึ่งก็คือ packed word 16 บิตสำหรับ RGB565 หรือ integer 8 บิตสำหรับ grayscale ซึ่งเป็นรูปแบบที่มีประสิทธิภาพเมื่อสีถูกคำนวณที่อื่นและอยู่ใน stored form แล้ว
การละเว้น argument color จะทาสีขาว ค่าเริ่มต้นนั้นสะดวกสำหรับงาน grayscale ซึ่งสีขาวคือค่าสูงสุดและอ่านได้ชัดเจนในพื้นหลังส่วนใหญ่ สำหรับ debug overlay ของ RGB565 นั้นมักจะผิดเสมอ สีเขียวหรือสีแดงมักอ่านได้ดีกว่าในฉากที่กล้องเห็นจริง และสีที่ชัดเจนสื่อถึงเจตนาได้ดี
5.6.3. ความหนาและการเติมสี¶
geometric method ส่วนใหญ่รับ flag สองตัวที่กำหนดวิธีการวาดเครื่องหมาย:
thickness=Nกำหนดความกว้างเส้นเป็นพิกเซล ค่าเริ่มต้นคือ1ซึ่งเหมาะสำหรับ overlay ส่วนใหญ่ ค่าที่ใหญ่กว่ามีประโยชน์เมื่อ annotation ต้องมองเห็นได้ชัดในฉากที่ซับซ้อน หรือหลังจากขั้นตอนถัดไปของ pipeline ปรับเปลี่ยนภาพเพิ่มเติมfill=Trueเปลี่ยนเครื่องหมายจากแบบ outline เป็นแบบทึบ โดยทาสีทุกพิกเซลภายในด้วยสีที่เลือก ค่าเริ่มต้นคือFalse
flag เหล่านี้ไม่นำไปใช้กับ primitives ที่ไม่มีส่วนภายในให้เติม ได้แก่ เส้น, เครื่องหมายบวก, ลูกศร, รูปสี่เหลี่ยมด้านไม่เท่า ซึ่งมีเพียง thickness ที่มีความหมาย
5.6.4. การวาดข้อความ¶
draw_string() เขียนตัวอักษรออกจาก bitmap font 8 x 10 พิกเซลที่มีอยู่ภายใน x และ y คือมุมซ้ายบนของ cell ตัวอักษรแรก text คือ string ที่ต้องการวาด และ color ปฏิบัติตามรูปแบบเดียวกับ geometric method font นั้นมี ASCII ทั้งหมดที่พิมพ์ได้และไม่มี kerning -- แต่ละตัวอักษรครอบครอง cell ขนาด 8 พิกเซลเท่ากัน -- ซึ่งทำให้ output ง่ายต่อการจัดตำแหน่ง
img.draw_string(10, 10, "blobs: 3", color=(0, 255, 0))
string สามารถมี newline (\n) ได้ แต่ละ newline จะย้ายตัวอักษรถัดไปไปยังจุดเริ่มต้นของบรรทัดใหม่ที่ต่ำกว่าบรรทัดก่อนหน้าสิบพิกเซล argument scale จะวาดแต่ละตัวอักษรในขนาดที่ใหญ่ขึ้นตามค่า floating-point และ x_spacing กับ y_spacing จะเพิ่ม padding รอบแต่ละตัวอักษร ชุด flag rotate / mirror / flip ขนาดเล็กสามารถนำไปใช้กับทั้ง string หรือแต่ละตัวอักษรเองได้ ซึ่งมีการควบคุมเพียงพอที่จะวางข้อความตามแนวเอียงหรือขอบที่ไม่แนวนอนเมื่อ layout ต้องการ
5.6.5. การล้าง canvas¶
method หนึ่งในกลุ่มนี้ไม่ได้วาดเครื่องหมายใดๆ โดยเฉพาะ มันเพียงแค่ reset ทุกพิกเซลของภาพให้เป็นศูนย์:
clear()-- ทำให้ทุกพิกเซลเป็นศูนย์ โดยสามารถจำกัดให้อยู่ใน ROI หรือกำหนดขอบเขตผ่าน mask ได้
clear() คือคำตอบที่ถูกต้องเมื่อแอปพลิเคชันกำลังสร้าง annotation ตั้งแต่ต้นทุกเฟรม -- เริ่มด้วย canvas สีดำ, วาด annotations ใหม่, ส่งผลลัพธ์ไปยัง display -- แทนที่จะซ้อนทับบน captured frame นอกจากนี้ยังเป็นวิธีที่ถูกที่สุดในการเตรียม scratch image สำหรับใช้เป็น mask buffer
ภาพที่ถูกจัดสรรใหม่มีค่าเป็นศูนย์แล้วจาก constructor ดังนั้น clear() จึงมีความสำคัญโดยเฉพาะสำหรับ buffer ที่นำมาใช้ซ้ำระหว่างเฟรม