Tuesday, April 13, 2010

Learning OpenCV: IplImage in OpenCV short guide

มาถึงข้อมูลที่น่าจะใชกันบ่อยจริงๆ ใน OpenCV นั่นคือ IplImage คอนเซปต์โดยรวมก็เหมือนกับ CvMat (ก็เหมือนถ่ายทอดกันมา) เพียงแต่ใน header มีข้อมูล พวก channel, bit depth, color mode และอื่นๆ ที่น่าจะเข้าใจไม่ยาก ลองยกตัวอย่างวิธีการเข้าถึงข้อมูลแต่ละ pixel ใน IplImage กัน


void saturate_sv( IplImage* img ) {
    for( int y=0; yheight; y++ ) {
        uchar* ptr = (uchar*) (
        img->imageData + y * img->widthStep
        );
       for( int x=0; xwidth; x++ ) {
          ptr[3*x+1] = 255;
          ptr[3*x+2] = 255;
      }
   }
}

เป็นฟังก์ชันเซตค่า image ในโหมด HSV ที่มี 3 channel ให้เพิ่มค่า S, V สูงสุด โดยไม่เปลี่ยน H

สังเกตวิธีการเข้าถึงทุก pixel ในภาพ การวนลูปจะวนตามแถว(row) ซึ่งจำนวน row มีค่าเท่ากับ height (ไม่งงใช่ไหม) ถ้าวนลูปกลับกันการเข้าถึงข้อมูลจะกระโดดไปมา และสังเกตว่าในแต่ละ row จะมีข้อมูลเฉพาะของแต่ละแถวอยู่ ขนาดข้อมูลส่วนนี้อยู่รวมอยู่ใน widthStep (แบบเดียวกับ step ใน CvMat) ดังนั้นเวลา iteration แต่ละแถวค่าที่เพิ่มขึ้นจะต้องเป็น widthStep ไม่ใช่ width*channel*bit dept

ข้อมูลที่่สำคัญอีกอันหนึ่งของ IplImage ที่ไม่มีใน CvMat คือ ROI(region on interest) ซึ่งโอเปอร์เรชั่นส่วนมากใน OpenCV สนับสนุน ROI ด้วย ข้อมูลใน IplImage จะถูกประมวลผลเฉพาะในส่วนที่ตั้งค่าไว้ใน ROI เท่านั้น มีคำสั่งที่สำคัญสำหรับ ROI คือ

  • cvSetImageROI ตั้งค่า ROI
  • cvResetImageROI ยกเลิกการตัั้งค่า ROI
เราจำเป็นต้องยกเลิกการตั้งค่า ROI ด้วยเนื่องจาก ROI มีผลต่อฟังก์ชันการแสดงผล ถ้าไม่ยกเลิกการตั้งค่าคำสั่งแสดงผลเช่น cvShowImage จะแสดงเฉพาะส่วน ROI เท่านั้น


แต่การใช้ ROI มีข้อจำกัดที่ว่าสามารถใช้ได้ทีละครั้ง แต่มีเทคนิคการในการสร้าง ROI หลายๆ อันได้ โดยใช้ประโยชน์ของ widthStep ขั้นตอนมีดังนี้

  1. สร้าง IplImage header โดยยังไม่ต้องสร้าง data (ประโยชน์หนึ่งของการสร้างแยกกันระหว่าง header กับ data) โดยตั้งค่าต่างๆ (depth, channel)ให้เท่ากับภาพต้นฉบับ ยกเว้น width กับ height
  2. ตั้งค่า widthStep ของ sub image ให้ตรงกับของต้นฉบับ
  3. ตั้งค่า pointer data ของ sub image ให้ตรงกับจุดเริ่มต้น ROI ในภาพต้นฉบับที่เราสนใจ
การตั้งค่าข้อสามนั้นจะทำให้ภาพของเราเริ่มต้องที่ ROI จากต้นฉบับ (แต่ C ไม่รับรู้เนื่องจากเป็น pointer) และทุกครั้งที่ iteration ข้อมูลในแต่ละแถวจะสิ้นสุดที่ ROI (เนื่องจากเราตั้งค่า width ไว้ใน header ของ sub image) การ iteration ไปยังแถวถัดไปจะใช้ค่า widthStep ของต้นฉบับ ดังนั้นจะเริ่มต้นที่คอลัมภ์เดียวกันในแนวถัดไปเสมอ (อย่าลืมว่าพื้นที่ข้อมูลเป็นเส้นตรงและข้อมูลของ sub image ยังใช้ข้อมูลของภาพต้นฉบับอยู่) ลองดูตัวอย่างจากหนังสือ Learning OpenCV


IplImage *sub_img = cvCreateImageHeader(
   cvSize(
      interest_rect.width,
      interest_rect.height
   ),
   interest_img->depth,
   interest_img->nChannels
);
sub_img->origin = interest_img->origin;
sub_img->widthStep = interest_img->widthStep;
sub_img->imageData = interest_img->imageData +
   interest_rect.y * interest_img->widthStep +
   interest_rect.x * interest_img->nChannels;
.................// operation with sub_image here
cvReleaseImageHeader(&sub_img);



โดย interest_img คือภาพต้นฉบับ, interest_rect คือ ROI ที่สัมพันธ์กับต้นฉบับ sub_image จะเป็นภาพ ROI ของ interest_img โดยวิธีนี้สามารถสร้าง ROI ได้หลายๆ อันในครั้งเดียว

No comments:

Post a Comment