ในบทช่วยสอนนี้ ฉันจะแสดงวิธีแก้ไขปัญหาระดับเริ่มต้นของการมองเห็นคอมพิวเตอร์โดยใช้ไลบรารี OpenCV สำหรับ Python อาจารย์ของฉันเสนอปัญหาที่นี่สำหรับชั้นเรียนการประมวลผลภาพดิจิทัลทางวิศวกรรม

ไลบรารี OpenCV เป็นไลบรารีคอมพิวเตอร์วิทัศน์แบบโอเพ่นซอร์สที่มีชื่อเสียงที่สุด (http://www.opencv.org/) ซึ่งมีให้บริการสำหรับภาษาการเขียนโปรแกรมหลายภาษา ด้วยคุณสมบัติมากมาย เราจึงสามารถปรับเปลี่ยนภาพดิจิทัลได้ เช่น การแปลงทางเรขาคณิต การกรอง การปรับเทียบกล้อง การแยกคุณสมบัติ การตรวจจับวัตถุ ฯลฯ การติดตั้งและการตั้งค่าที่ใช้จะไม่แสดงที่นี่

การจัดการพิกเซล

สำหรับปัญหาแรก ในฐานะปัญหาประเภท "สวัสดีชาวโลก" เราจะเข้าถึงรูปภาพและจัดการพิกเซลของมัน

ขั้นแรกให้นำเข้าไลบรารี

ในการโหลดรูปภาพ เราใช้ฟังก์ชัน imread ที่ส่งผ่านเป็นพารามิเตอร์ชื่อไฟล์ที่จะอ่านและวิธีที่เราต้องการให้อ่าน: 0 - ขาวดำ, 1 - สี, -1 - ไม่เปลี่ยนแปลง สำหรับปัญหานี้ เราจะอ่านเป็นขาวดำและสีเท่านั้น ในการแสดงรูปภาพเหมือนกับการอ่าน เราจะเรียกฟังก์ชันที่ส่งเป็นพารามิเตอร์ ชื่อของหน้าต่างที่จะแสดงและเนื้อหา โดยปกติแล้วคือออบเจ็กต์รูปภาพที่เราสร้างขึ้นโดยการเรียก imread

ดังนั้น มาอ่านภาพขาวดำ เข้าถึงพิกเซลของมัน เปลี่ยนแปลงบางส่วนและแสดงผลลัพธ์กัน รูปภาพจะถูกโหลดเป็นเมทริกซ์ (แต่ละองค์ประกอบหนึ่งพิกเซล) หากปรับเป็นสีเทา เมทริกซ์จะมีสองมิติ โดยแต่ละองค์ประกอบจะแสดงถึงความเข้มของสีเทา หากเป็นสี จะเป็นชุดของเมทริกซ์ 3 ตัวที่แสดงถึงช่องสี RGB

หมายเหตุสำคัญ: ใน OpenCV พิกัดของเมทริกซ์จะแตกต่างกัน พิกัดแนวนอนคือ Y และ X แนวตั้ง นอกจากนี้ มุมบนซ้ายคือตำแหน่งที่วางจุดกำเนิด (0, 0) ดังนั้นพิกัดจะขยายจากมุมบนซ้ายไปขวาและทิศทางด้านล่าง เพื่อให้เข้าใจได้ง่ายขึ้น การวนซ้ำพิกัดโดยใช้ตัวแปรที่เรียกว่า X และ Y เราจะเปลี่ยนเป็นรูปแบบ OpenCV เมื่อเข้าถึงตำแหน่งเท่านั้น ตัวอย่างเพิ่มเติมในบทช่วยสอนนี้

หลังจากอ่านแล้ว เราสามารถเข้าถึงเมทริกซ์ได้ง่ายๆ และเปลี่ยนสีพิกเซลตามที่เราต้องการ หลังจากแก้ไขบางส่วนแล้ว เพื่อสร้างสี่เหลี่ยมให้เป็นสีดำ เราก็แสดงผล

จำเป็นต้องมีคำสั่งที่มีประโยชน์มากที่นี่ เพื่อให้เราสามารถดำเนินการหลายอย่างกับรูปภาพโดยใช้โค้ดเดียวกัน waitKey รอให้ผู้ใช้กดปุ่มเพื่อดำเนินการต่อในโค้ด (เพื่อให้เราสามารถเห็นผลลัพธ์ที่แสดงอย่างสงบ) พารามิเตอร์ 0 รอให้กดปุ่มใด ๆ เพื่อดำเนินการต่อในโค้ด หลังจากการประมวลผลนี้ เราก็อ่านภาพของเลนนาอีกครั้ง แต่คราวนี้ให้ระบายสีและสร้างสี่เหลี่ยมสีแดงเหมือนกับสีดำที่เราเคยทำมาก่อน

การเข้าถึงพิกเซลทำได้แตกต่างกันสำหรับรูปภาพสี อาร์เรย์ที่เราส่งไปยังพิกเซลแสดงถึงช่องสีตามลำดับ BGR (ใช่ มันถูกพลิกด้วยเหตุผลใดก็ตาม) เนื่องจากผลลัพธ์เรามีสี่เหลี่ยมสีแดงสว่างสูงสุดซึ่งสร้างขึ้นที่จุดเดียวกัน

สุดท้ายนี้ เพื่อทำลายหน้าต่างที่สร้างขึ้นเพื่อแสดงรูปภาพ เราเรียกฟังก์ชันนี้ว่า destroyAllWindows ดังนั้นจึงไม่มีอะไรแสดงขึ้นมาหลังจากที่โปรแกรมหยุดทำงาน

ตอนนี้ เรามาสร้างสี่เหลี่ยมผืนผ้าที่มีด้านลบของภาพขาวดำกันดีกว่า ผู้ใช้ระบุตำแหน่งของสี่เหลี่ยมผืนผ้า และเอฟเฟกต์เชิงลบจะถูกสร้างขึ้นโดยลบพิกเซลออกจากค่าสูงสุดที่เป็นไปได้สำหรับพิกเซล (8 บิต:255 ที่นี่)

เราได้รับพิกัดต่อไปนี้ 100, 200, 30 และ 150

สำรวจการจัดการพิกเซลเพิ่มเติม มาจัดลำดับสี่ควอแดรนท์ของรูปภาพกลับด้านกัน ควอแดรนท์แสดงถึง 1/4 ของรูปภาพในรูปแบบสี่เหลี่ยมจัตุรัส ราวกับว่ารูปภาพถูกตัดครึ่งในแนวตั้งและแนวนอน

สำหรับความท้าทายนี้ เราจะใช้ไลบรารี numpy เพื่อทำให้การจัดการกับเมทริกซ์ง่ายขึ้น ฟังก์ชัน numpy splitแยกเมทริกซ์ในตำแหน่งที่กำหนดและทิศทางที่กำหนด หลังจากแยกภาพแล้ว เราจะต้องต่อส่วนต่าง ๆ ตามลำดับกลับด้าน

เติมภูมิภาค

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

ในที่นี้เราถือว่าแต่ละการรวมพิกเซลสีขาวเป็นวัตถุเดียว ดังนั้นสิ่งที่สามารถทำได้คือการติดฉลาก โดยปกติแล้วอัลกอริทึมการติดป้ายกำกับจะมีรูปภาพไบนารีเป็นอินพุตและส่งกลับรูปภาพสีเทาหลายขนาด เพื่อจุดประสงค์นี้ เราจะใช้อัลกอริทึมในตัวสำหรับการเติมพื้นที่ที่เรียกว่า floodfill อัลกอริธึมนี้จะค้นหาเพื่อนบ้านที่มีสีเดียวกันโดยใช้เมล็ดพิกเซลเป็นข้อมูลอ้างอิง ถ้าเรากำหนดสีเป็นพารามิเตอร์ ฟังก์ชันจะทำให้พิกเซลทั้งหมดที่พบจากการค้นหามีสีนั้น

floodFillจาก opencv จะขอรูปภาพ, มาสก์ (ไม่ได้ใช้ = ไม่มี), พิกเซลของเมล็ดพืช และสีสำหรับใส่สีเมล็ดพืชและเพื่อนบ้านที่คล้ายกัน ด้วยเหตุนี้ เราจึงต้องตรวจสอบสีของมันทีละพิกเซลเท่านั้น หากเป็นสีขาวเราจะเปลี่ยนเป็นสีเทาบางส่วน จากนั้นเพิ่มสีเทาเพื่อให้วัตถุถัดไปมีโทนสีเทาที่แตกต่างกัน ในระหว่างกระบวนการนี้ เราจะสามารถนับจำนวนครั้งที่มีการใช้ floodFill และถือว่านั่นคือจำนวนวัตถุที่เรามีในฉาก

เป็นผลให้เราไม่สามารถมองเห็นวัตถุบางอย่างได้เนื่องจากมาตราส่วนสีเทาที่ใช้เป็นตัวแปรเดียวกับที่ใช้ในการนับจำนวนวัตถุ จึงทำให้เป็นสีเทาเข้มมาก

เนื่องจากเราใช้ระดับสีเทาเพื่อนับจำนวนวัตถุ เราจึงถูกจำกัดด้วยค่าของวัตถุ 255 ชิ้นในฉากสำหรับการประมวลผล 8 บิต คุณสามารถนึกถึงวิธีต่างๆ ในการแก้ปัญหานี้ ฉันขอแนะนำให้สร้างตัวแปรอื่นที่ติดตามเมื่อจำนวนองค์ประกอบถึง 256 (เริ่มจาก 0) เมื่อสิ่งนั้นเกิดขึ้น ตัวแปรนี้จะเพิ่มขึ้นและ nelemจะเป็นศูนย์เพื่อเริ่มต้นใหม่ ในตอนท้ายของการติดฉลาก เราจะได้วัตถุที่มีสีเทาเหมือนกัน แต่ตัวแปรที่ติดตามร่วมกับ nelem สามารถคำนวณได้ว่ามีวัตถุอีกกี่ชิ้นที่ตรวจพบหลังจากขีดจำกัด

ตอนนี้เมื่อเห็นภาพไบนารี่ดั้งเดิมอีกครั้ง เราก็สามารถรู้ได้ว่ามีวัตถุสองประเภทที่แตกต่างกัน มีรูและไม่มีรู เราจะนับแยกกันได้อย่างไร?

ก่อนอื่นเราไม่รู้ว่าอันที่ตัดตามขอบนั้นมีรูหรือเปล่า ดังนั้นเราจึงเพิกเฉยต่อพวกมัน (อย่างใด) การมีวัตถุทั้งหมดในฉาก วิธีหนึ่งในการแก้ปัญหานี้คือการใช้ floodFillในพื้นหลังของภาพให้เป็นสีขาว 255 (ในกรณีที่ฉากมีวัตถุมากกว่า 255 ชิ้น เราจะต้องแก้ไขโค้ดเป็น ป้องกันแม็กซ์ไวท์ไม่ให้ถูกนำมาใช้) ตอนนี้เรามีวัตถุที่เป็นสีเทา พื้นหลังเป็นสีขาว และหลุมเป็นสีดำ (พื้นหลังเก่า) เนื่องจากน้ำท่วมที่เติมพื้นหลังเป็นสีขาวจะไม่โดนหลุม

ด้วยวิธีนี้ เราจึงแก้ไขปัญหาได้ โดยการมีรูเป็นสีดำ เพียงแค่ค้นหาพิกเซลสีดำทั่วทั้งภาพ และสำหรับแต่ละพิกเซลสีดำที่พบ เราจะเพิ่มจำนวนรู จากนั้นจึงเติมให้เต็มเพื่อไม่ให้เราเข้าใจผิดกับ ผลรวม สำหรับตัวอย่างนี้ เราทำด้วยสีพื้นหลังเดียวกัน ดังนั้นเราจึงรู้สึกเหมือนเป็นหลุม

เพียงเท่านี้ ในส่วนแรกนี้ ฉันพยายามแสดงพื้นฐานบางอย่างเพื่อทำความเข้าใจวิธีการจัดวางภาพดิจิทัล และเราสามารถเข้าถึงพิกเซลของภาพและทำการวิเคราะห์ได้อย่างไร ฉันหวังว่ามันจะชัดเจนเพียงพอ :)

ขอบคุณสำหรับการอ่านเพื่ออ่านเพิ่มเติมไป:

2. ความรู้เบื้องต้นเกี่ยวกับ OpenCV ด้วย Python ตอนที่ II

3. การปรับปรุงการรับแสงโดยใช้ Fourier Transform ด้วย OpenCV

4. มาเล่นกับเส้นขอบรูปภาพบน OpenCV กันเถอะ

5. การหาปริมาณสีด้วย Kmeans ใน OpenCV

รหัสทั้งหมดสามารถพบได้ที่พื้นที่เก็บข้อมูลสาธารณะของฉันที่ "GitHub"