Thursday, March 2, 2017

Lookup table และการวัดเวลาในการประมวลผลคำสั่ง

การทำ pixel processing สามารถใช้ lookup table เพื่อมาแก้ปัญหาได้ ตัว lookup table ก็คือตารางค่าที่มีการคำนวณไว้ล่วงหน้าแล้ว ถ้าสมมติว่าพิกเซลของรูปต้นฉบับมีค่าเท่าไหร่ เราก็สามารถเอาไปเทียบใน lookup table เพื่อหาผลลัพธ์ได้ทันที วิธีนี้ก็จะประหยัดเวลาในการคำนวณ โดยเฉพาะถ้าการคำนวณนั้นซับซ้อนไปได้มาก

ลองเทียบดูกับโจทย์ image negative ในตัวอย่างที่แล้ว เราต้องการคำนวณพิกเซลใหม่ โดย 255 - พิกเซลเก่า ซึ่งต้องทำทุกๆพิกเซล แต่เนื่องจากว่าพิกเซลมีค่าแค่ 256 ค่า คือจาก 0-255 ดังนั้น ถ้าสร้าง lookup table ไว้แบบนี้

index : 0       1      2       3    ... 255
value : 255  254  253  252  ...   0

ถ้าพิกเซลต้นฉบับมีค่าเป็น 0 ก็เอาไปเทียบในตารางข้างต้น จะได้ผลลัพธ์เป็น 255 ทันทีโดยไม่ต้องคำนวณ

สมมติว่ารูปมีขนาด 256x256 พิกเซล หรือทั้งหมด 65536 พิกเซล แทนที่จะคำนวณทุกพิกเซล ก็เหลือแค่การคำนวณเฉพาะ 256 พิกเซลใน lookup table ที่เหลือก็แค่เปรียบเทียบ ซึ่งจะประหยัดเวลาไปได้มาก

ดังนั้น โค้ดการใช้ lookup table กับปัญหา image negative ก็จะประมาณนี้

//need to convert original matrix to have the same type of output matrix
mat1.convertTo(mat1, CvType.CV_8UC3);
//create lookup matrix 1 row 256 columns (index 0-255)
Mat lookup = Mat.zeros(1, 256, CvType.CV_8UC1);
//an array of 256 members, index 0 is for pixel value 0
double[] temp = new double[256];
//loop to assign inverse to array
for(int i=0;i<256;i++) {
 temp[i] = 255-i;
}
//put array to lookup matrix
lookup.put(0, 0, temp);
//perform lookup table processing for all channels of mat1 and put result into mat2
Core.LUT(mat1, lookup, mat2);

แล้วเราจะทราบได้อย่างไรว่ามันใช้เวลาประมาณเท่าไหร่
หลักการก็ง่ายๆ วัดเวลาก่อน process กับหลัง process เสร็จ แล้วหาความต่างของเวลา ด้วยโค้ด

long tstart = System.nanoTime();
//------------------------
//our algorithm here
//------------------------
long tend = System.nanoTime();
double elapse = (tend-tstart)/1000000000.0;
Log.i("time", "Processing time = "+elapse+" seconds");

ผมลองวัดเวลาโดยประมาณของแต่ละวิธีในการทำ image negative ในมือถือที่ใช้ทดสอบ ได้ประมาณนี้ครับ
1. ใช้คำสั่ง
Core.bitwise_not(mat1, mat2);
ใช้เวลาเฉลี่ย 0.0072 วินาที

2. ใช้คำสั่ง
mat1.convertTo(mat2, CvType.CV_8UC3, -1, 255);
ใช้เวลาเฉลี่ย 0.027 วินาที

3. ใช้การวนลูปแถวและคอลัมน์ ใช้เวลาเฉลี่ย 3.35 วินาที

4. ใช้การ dump ไปประมวลผลใน array  ใช้เวลาเฉลี่ย 0.142 วินาที

5. ใช้ lookup table ข้างต้น  ใช้เวลาเฉลี่ย 0.0103 วินาที

เรียงตามลำดับความเร็ว คือ วิธีที่ 1 5 2 4 3

ลองพิจารณาดูแต่ละวิธี แล้วเลือกใช้งานตามความชอบเลยครับ ส่วนถ้าให้ผมเลือก ผมคงเลือกคำสั่งที่ OpenCV มีให้ก่อน เช่น วิธีที่ 1 หรือ 2 ถ้าใช้ไม่ได้ค่อยลองวิธี 5 4 และ 3 ตามลำดับครับ

No comments:

Post a Comment