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 target

  • draw_arrow() -- ลูกศรจากจุดเริ่มต้นไปยังจุดสิ้นสุด

  • draw_edges() -- ด้านทั้งสี่ของรูปสี่เหลี่ยมด้านไม่เท่า กำหนดโดยจุดมุมทั้งสี่ วิธีธรรมชาติในการร่างขอบ tag ที่ตรวจจับได้หรือบริเวณที่ถูก warp ด้วย perspective

  • draw_string() -- ข้อความจาก bitmap font ที่มีอยู่ภายใน

ทุก method เหล่านี้จะปรับเปลี่ยนภาพแหล่งที่มา in place และคืนค่าภาพเดิมสำหรับการ chain ตามรูปแบบ operating-method ที่กำหนดไว้ก่อนหน้า

A grid of small panels showing each of the eight drawing primitives applied once. Each panel contains a line, a rectangle, a circle, an ellipse, a cross, an arrow, a quadrilateral, or a short text string, with the name of the method that produced it labelled underneath.

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 ที่นำมาใช้ซ้ำระหว่างเฟรม