ลอง compile ซอร์สโค้ด OpenCV 2.31 ให้ support QT+OpenGL ครับ เห็นอินเตอร์เฟสที่โชว์ในเว็บแล้วสวยดี
แต่ๆๆๆ ผลลัพธ์ที่ได้
มีข้อความว่า ตอนนี้ไม่สามารถใช้ OpenGL rendering กับการกำหนดสัดส่วนของหน้าต่างได้ ขนาดของรูปที่เล็กกว่า menu bar ด้านบน จึงทำได้รูปเบี้ยวไม่ได้สเกล :( ถ้ารูปกว้างกว่าแถบ menu ก็จะไม่เบี้ยว
ข้อดี
มีเมนูบาร์ สำหรับการ pan ซ้ายขวา บนล่าง ซูมเข้าออก เซฟผลลัพธ์ รวมถึงถ้าวางเมาส์บนรูป จะแสดงพิกัด x y และค่าพิกเซล
ข้อควรปรับปรุง
บักที่ทำให้รูปที่เล็กกว่าเมนูบาร์เบี้ยว
Wednesday, December 21, 2011
Visual C++ Express 2010 กับ OpenCV 2.31
ถึงเวลาที่ต้องมาใช้ VC++ จนได้ เพราะอยากเอาโค้ดไปเชื่อมกับ Matlab ผ่าน mex file และต้องเชื่อมกับ OpenCV ต่อไป
วิธีการติดตั้ง VC++ เพื่อให้ใช้ได้กับ OpenCV ดูตาม http://opencv.willowgarage.com/wiki/VisualC%2B%2B_VS2010_CMake ได้ครับ ถ้ามีเวลาคงมาสรุปเป็นภาษาไทยอีกครั้ง
ผลการทำพบว่า คอมไพล์ซอร์สโค้ดก็ไม่ยากครับ แต่ไม่สามารถสร้างตัวไบนารีแบบ install ได้ ผมเข้าใจว่ามี bug เล็กน้อยใน makefile ตอนกำหนดไดเรกทอรี อย่างไรก็ตามถ้าก๊อปไฟล์เองก็ใช้งานได้
ปัญหาถัดมา เมื่อทดสอบกับซอร์สโค้ด OpenCV ทำการ build ผ่านไม่มีปัญหาครับ แต่ว่าตอน Debug ไม่ผ่าน ต้องไปรันไฟล์ต่างหากเอง ไม่รู้ว่าเพราะอะไร เซิร์ชหาก็ยังไม่เจอวิธีแก้ครับ คงต้องหลับหูหลับตาผ่านไปก่อน
ข้อดีของ Visual C++ กับ OpenCV
-ตัว auto complete ของ IDE VC++ นี่ดีจริงๆ แต่อืดเหมือนกัน
-Build ง่าย เลือก Debug หรือ Release เวอร์ชันก็ง่าย ขนาดไฟล์ผลลัพธ์เล็ก
-มีคนใช้เยอะ พอเจอปัญหาน่าจะหาทางแก้ง่าย
ปัญหาที่เจอ
-ทำตามคู่มือแล้วแต่คอมไพล์แบบ install ยังไม่สมบูรณ์
-Debug ไม่ผ่าน :( สงสัยผมจะลืมขั้นตอนอะไรไป
วิธีการติดตั้ง VC++ เพื่อให้ใช้ได้กับ OpenCV ดูตาม http://opencv.willowgarage.com/wiki/VisualC%2B%2B_VS2010_CMake ได้ครับ ถ้ามีเวลาคงมาสรุปเป็นภาษาไทยอีกครั้ง
ผลการทำพบว่า คอมไพล์ซอร์สโค้ดก็ไม่ยากครับ แต่ไม่สามารถสร้างตัวไบนารีแบบ install ได้ ผมเข้าใจว่ามี bug เล็กน้อยใน makefile ตอนกำหนดไดเรกทอรี อย่างไรก็ตามถ้าก๊อปไฟล์เองก็ใช้งานได้
ปัญหาถัดมา เมื่อทดสอบกับซอร์สโค้ด OpenCV ทำการ build ผ่านไม่มีปัญหาครับ แต่ว่าตอน Debug ไม่ผ่าน ต้องไปรันไฟล์ต่างหากเอง ไม่รู้ว่าเพราะอะไร เซิร์ชหาก็ยังไม่เจอวิธีแก้ครับ คงต้องหลับหูหลับตาผ่านไปก่อน
ข้อดีของ Visual C++ กับ OpenCV
-ตัว auto complete ของ IDE VC++ นี่ดีจริงๆ แต่อืดเหมือนกัน
-Build ง่าย เลือก Debug หรือ Release เวอร์ชันก็ง่าย ขนาดไฟล์ผลลัพธ์เล็ก
-มีคนใช้เยอะ พอเจอปัญหาน่าจะหาทางแก้ง่าย
ปัญหาที่เจอ
-ทำตามคู่มือแล้วแต่คอมไพล์แบบ install ยังไม่สมบูรณ์
-Debug ไม่ผ่าน :( สงสัยผมจะลืมขั้นตอนอะไรไป
Thursday, December 15, 2011
งงกับการ link ของ g++
ทดลองคอมไพล์ซอร์สโค้ดของคนอื่น ด้วย g++ (mingw) บนวินโดวส์
Makefile ของเจ้าของเป็นบน unix
ส่วนของการคอมไพล์ไม่มีปัญหา พอมาลิงก์ แสดงผลว่า undefined reference xxx
อะไรเนี่ย นั่งงมเปิดนั่น โหลดนี่ เซิร์ชหาคำตอบทั่วเน็ต ยอมแม้กระทั่งคอมไพล์ไลบรารีตัวอื่นที่เกี่ยวข้องใหม่หมด
สุดท้าย คำตอบอยู่ที่การเรียงลำดับคำสั่งของ g++
คอมไพล์
g++ -Ixxx -c input.cpp -o output.o
เหมือนกันทั้ง unix และ windows (xxx คือตำแหน่งของ include files)
ลิงก์ (สมมติว่าสร้าง dynamic library)
-unix
g++ -Lyyy -lzzz -shared output.o -o libxyz.so
-windows
g++ -shared output.o -o libxyz.so -Lyyy -lzzz
(yyy คือตำแหน่งของ library file, zzz คือชื่อ library)
ต่างกันแค่เนี้ยยยย งมไปงมมาจนมึน
สุดท้ายใช้การได้ แต่เจ้าของโค้ดดันออกแบบให้ได้เฉพาะแบบของเขาซะอีก ต้องไปแก้โค้ดอีกเยอะเลย เฮ้อ
ไม่ใช้ซะดีไหมเนี่ย
Makefile ของเจ้าของเป็นบน unix
ส่วนของการคอมไพล์ไม่มีปัญหา พอมาลิงก์ แสดงผลว่า undefined reference xxx
อะไรเนี่ย นั่งงมเปิดนั่น โหลดนี่ เซิร์ชหาคำตอบทั่วเน็ต ยอมแม้กระทั่งคอมไพล์ไลบรารีตัวอื่นที่เกี่ยวข้องใหม่หมด
สุดท้าย คำตอบอยู่ที่การเรียงลำดับคำสั่งของ g++
คอมไพล์
g++ -Ixxx
เหมือนกันทั้ง unix และ windows (xxx คือตำแหน่งของ include files)
ลิงก์ (สมมติว่าสร้าง dynamic library)
-unix
g++ -Lyyy
-windows
g++ -shared output.o -o libxyz.so -Lyyy
ต่างกันแค่เนี้ยยยย งมไปงมมาจนมึน
สุดท้ายใช้การได้ แต่เจ้าของโค้ดดันออกแบบให้ได้เฉพาะแบบของเขาซะอีก ต้องไปแก้โค้ดอีกเยอะเลย เฮ้อ
ไม่ใช้ซะดีไหมเนี่ย
Saturday, November 26, 2011
Speed Test
วันนี้ว่างๆเลยลองมาทดสอบความเร็ว (แบบบ้านๆ) กับการเข้าถึงพิกเซลของสามโปรแกรม คือ MATLAB, OpenCV (C++) และ ImageJ (JAVA) โดยใช้รูปขนาด 256x256 pixels มาทำ image negative (inverse) ด้วยโค้ดปกติของแต่ละอัน นั่นคือ
ต่อมา ทดลองเพิ่มขนาดรูปเป็น 2550x1900 พิกเซล เวลาโดยประมาณเป็น 0.006, 0.05, 0.12 วินาทีตามลำดับ
เห็นตัวเลขแล้วแปลกๆ ทำไม MATLAB ถึงได้เร็วขนาดนี้ แสดงว่าถ้าประมวลผลทีละพิกเซล MATLAB มีการ optimize โค้ดแบบเมตริกซ์ที่ดีมาก
โอกาสต่อไปจะลองทำการ convolution แล้วมาเทียบกันดูครับ
- MATLAB ใช้ J = 255-I;
- OpenCV และ ImageJ ใช้ลูป 1 ชั้น ประมวลผลรูปแบบเวกเตอร์
ต่อมา ทดลองเพิ่มขนาดรูปเป็น 2550x1900 พิกเซล เวลาโดยประมาณเป็น 0.006, 0.05, 0.12 วินาทีตามลำดับ
เห็นตัวเลขแล้วแปลกๆ ทำไม MATLAB ถึงได้เร็วขนาดนี้ แสดงว่าถ้าประมวลผลทีละพิกเซล MATLAB มีการ optimize โค้ดแบบเมตริกซ์ที่ดีมาก
โอกาสต่อไปจะลองทำการ convolution แล้วมาเทียบกันดูครับ
Saturday, October 1, 2011
Flip image & display
วันนี้ลองมาใช้ฟังก์ชันง่ายๆ เพื่อ flip รูปกันครับ
ผลลัพธ์ที่ต้องการ
เช่นเดียวกันครับ ถือว่ารูปอยู่ที่เดียวกับไฟล์ output
สังเกตว่ามีการสร้าง 2 หน้าต่างเพื่อแสดง input และ output
มีการตรวจสอบว่าอ่านรูปได้หรือไม่ โดย if(!image.data)
และมีการกลับรูปโดยใช้คำสั่ง flip(image, result, 1); //1=horizontal, 0=vertical, -1=both
จะเห็นว่า OpenCV2 พยายามทำให้รูปแบบการใช้งานคำสั่งต่างๆ ดูง่ายขึ้นกว่ารุ่นก่อนๆครับ
ผลลัพธ์ที่ต้องการ
เช่นเดียวกันครับ ถือว่ารูปอยู่ที่เดียวกับไฟล์ output
#include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> using namespace cv; int main() { //Read file and load to variable Mat image = imread("lenag.jpg"); //If read failure if(!image.data) { return -1; } //Create a window to display the image namedWindow("Original image"); //Show the image imshow("Original image",image); //Create another image for result Mat result; flip(image, result, 1); //1=horizontal, 0=vertical, -1=both //Create a window to display the image namedWindow("Output image"); //Show the image imshow("Output image",result); //Wait for user to press any key waitKey(0); return 0; }
สังเกตว่ามีการสร้าง 2 หน้าต่างเพื่อแสดง input และ output
มีการตรวจสอบว่าอ่านรูปได้หรือไม่ โดย if(!image.data)
และมีการกลับรูปโดยใช้คำสั่ง flip(image, result, 1); //1=horizontal, 0=vertical, -1=both
จะเห็นว่า OpenCV2 พยายามทำให้รูปแบบการใช้งานคำสั่งต่างๆ ดูง่ายขึ้นกว่ารุ่นก่อนๆครับ
Friday, September 30, 2011
OpenCV 2.31 & MinGW (Dev-C++) Part 3
มาทดสอบ library ที่เราคอมไพล์แล้วดูครับ
ตอนนี้เราก็จะมีโฟลเดอร์ C:\OpenCV ที่เก็บ library ที่ต้องใช้ในการเขียนโปรแกรมของเรา (bin,include,lib,doc) ให้เซ็ต path ของ windows ไปที่ C:\OpenCV\bin ด้วยครับ
เปิด Dev-C++ เลยครับ
เลือกเมนู Tools / Compiler Options แล้วเพิ่มในส่วนของ linker ตามนี้ครับ
-lopencv_core231 -lopencv_highgui231 -lopencv_imgproc231 -lopencv_features2d231 -lopencv_calib3d231 บางตัวก็อาจจะไม่ได้ใช้สำหรับทุกโปรแกรม แต่ก็ไม่เป็นไรครับ มีเกินไว้ก่อนก็ได้
จากนั้นก็กำหนดแทบ Directories ต่างๆตามนี้ครับ
-Binaries
C:\mingw\bin
C:\OpenCV\bin
-Libraries
C:\mingw\lib
C:\OpenCV\lib
-C++ Includes
C:\mingw\include
C:\OpenCV\include
ตอนนี้เราก็พร้อมที่จะเขียนโปรแกรมทดสอบแล้วครับ
สมมติว่ามีไฟล์ lenag.jpg อยู่ที่เดียวกับตัวไฟล์ผลลัพธ์ของโปรแกรมนะครับ
ผลลัพธ์ที่น่าจะได้
เอกสารอ้างอิง
http://opencv.itseez.com/doc/tutorials/introduction/display_image/display_image.html#display-image
ตอนนี้เราก็จะมีโฟลเดอร์ C:\OpenCV ที่เก็บ library ที่ต้องใช้ในการเขียนโปรแกรมของเรา (bin,include,lib,doc) ให้เซ็ต path ของ windows ไปที่ C:\OpenCV\bin ด้วยครับ
เปิด Dev-C++ เลยครับ
เลือกเมนู Tools / Compiler Options แล้วเพิ่มในส่วนของ linker ตามนี้ครับ
-lopencv_core231 -lopencv_highgui231 -lopencv_imgproc231 -lopencv_features2d231 -lopencv_calib3d231 บางตัวก็อาจจะไม่ได้ใช้สำหรับทุกโปรแกรม แต่ก็ไม่เป็นไรครับ มีเกินไว้ก่อนก็ได้
จากนั้นก็กำหนดแทบ Directories ต่างๆตามนี้ครับ
-Binaries
C:\mingw\bin
C:\OpenCV\bin
-Libraries
C:\mingw\lib
C:\OpenCV\lib
-C++ Includes
C:\mingw\include
C:\OpenCV\include
ตอนนี้เราก็พร้อมที่จะเขียนโปรแกรมทดสอบแล้วครับ
สมมติว่ามีไฟล์ lenag.jpg อยู่ที่เดียวกับตัวไฟล์ผลลัพธ์ของโปรแกรมนะครับ
ผลลัพธ์ที่น่าจะได้
เอกสารอ้างอิง
http://opencv.itseez.com/doc/tutorials/introduction/display_image/display_image.html#display-image
OpenCV 2.31 & MinGW (Dev-C++) Part 2
เมื่อจะคอมไพล์ source code เอง ก็ต้องมีเครื่องมือให้พร้อมครับ
เดิมทีผมคิดว่าคงเป็นการยาก ไม่ทำดีกว่า แต่ได้อ่านในคู่มือ http://opencv.willowgarage.com/wiki/MinGW ก็น่าจะทำได้
โปรแกรมที่ต้องการนะครับ
จากนั้น กำหนดค่าของสองช่องด้านบน
ช่องแรก browse ไปที่ source code ของ OpenCV เช่น C:\OpenCV231Source
ช่องสอง browse ไปที่โฟลเดอร์ใหม่ที่ต้องการเก็บผลลัพธ์ของการคอมไพล์ เช่น C:\OpenCV231
จากนั้นคลิกปุ่ม Configure ครับ เลือกตามรูป
จากนั้นก็ browse เลือกคอมไพเลอร์ครับ
แล้วก็ finish
ในขณะนี้โปรแกรมก็จะทำการตรวจสอบ เช็คค่าพารามิเตอร์ต่างๆครับ
ถ้าเสร็จแล้ว ในช่องข้อความล่างสุดแสดงคำว่า configuring done ก็เป็นอันใช้ได้ครับ จะเกิดแถบสีแดงๆตรงช่องกลางเต็มไปหมด
ในคู่มือบอกว่า ให้กำหนดค่าเพิ่มหนึ่งค่าคือ CMAKE_BUILD_TYPE ให้เป็น Releaseตามรูปครับ
จากนั้นก็กด Configure อีกครั้ง แถบสีแดงก็จะหายไป
ให้กด Generate ถ้าเสร็จแล้ว ปิด CMake ได้
ไปที่ dos prompt แล้ว เข้าไปที่ไดเรกทอรีผลลัพธ์ เช่น C:\OpenCV231
พิมพ์ mingw32-make แล้วรอๆๆๆๆๆ นานอยู่ครับ
ความจริงถึงขั้นตอนนี้ก็เสร็จแล้วครับ แต่ถ้าอยากได้แค่ตัวหลักๆสำหรับการใช้งาน ให้ใช้ dos prompt ที่ไดเรกทอรีเดิม แล้วพิมพ์ mingw32-make install
จากนั้น จะพบว่าในไดเรกทอรีย่อย install จะมีไฟล์ทุกอย่างพร้อมใช้งานครับ ซึ่งหลักๆก็จะมีไดเรกทอรีย่อย bin, lib, include,doc ซึ่งสามารถแจกจ่ายให้คนที่ไม่ต้องการคอมไพล์เองก็ได้
สมมติว่าเราจะสำเนาทุกๆไฟล์ในไดเรกทอรี install นี้ไปไว้ยัง C:\OpenCV เพื่อใช้งานต่อไปนะครับ
เป็นอันว่าเราได้คอมไพล์และลิงก์ OpenCV สำเร็จ ได้ library ต่างๆที่เราต้องการแล้วครับ
เอกสารอ้างอิง
http://opencv.willowgarage.com/wiki/MinGW
http://www.cmake.org
http://www.mingw.org/
เดิมทีผมคิดว่าคงเป็นการยาก ไม่ทำดีกว่า แต่ได้อ่านในคู่มือ http://opencv.willowgarage.com/wiki/MinGW ก็น่าจะทำได้
โปรแกรมที่ต้องการนะครับ
- MinGW (gcc) ความจริงตัวนี้ก็มีใน Dev-C++ ครับ แต่ว่าเก่าแล้ว เลยไปดาวน์โหลดตัวใหม่กว่ามา มีให้เลือกหลายที่ครับ ผมใช้ http://get.qt.nokia.com/misc/MinGW-gcc440_1.zip เพราะว่าจะลองทดสอบกับ Qt ด้วยภายหลัง สมมติว่าโหลดมาแล้วและก็แตกไว้ที่ C:\mingw นะครับ ให้เซ็ต path ของ windows ไปที่ C:\mingw\bin ด้วยนะครับ
- OpenCV source ก็ได้จากการแตกไฟล์ที่ดาวน์โหลดมาคือ OpenCV-2.3.1-win-superpack.exe ครับ สมมติว่าอยู่ที่ C:\OpenCV231Source
- CMake สำหรับสร้าง makefile เพื่อคอมไพล์ครับ จะช่วยให้คนที่ไม่รู้เรื่องในการคอมไพล์เช่นผมทำงานได้ง่ายขึ้นมาก ดาวน์โหลดที่นี่ครับ http://www.cmake.org/files/v2.8/cmake-2.8.5-win32-x86.zip สมมติว่าแตกไว้ที่ C:\CMake
จากนั้น กำหนดค่าของสองช่องด้านบน
ช่องแรก browse ไปที่ source code ของ OpenCV เช่น C:\OpenCV231Source
ช่องสอง browse ไปที่โฟลเดอร์ใหม่ที่ต้องการเก็บผลลัพธ์ของการคอมไพล์ เช่น C:\OpenCV231
จากนั้นคลิกปุ่ม Configure ครับ เลือกตามรูป
จากนั้นก็ browse เลือกคอมไพเลอร์ครับ
แล้วก็ finish
ในขณะนี้โปรแกรมก็จะทำการตรวจสอบ เช็คค่าพารามิเตอร์ต่างๆครับ
ถ้าเสร็จแล้ว ในช่องข้อความล่างสุดแสดงคำว่า configuring done ก็เป็นอันใช้ได้ครับ จะเกิดแถบสีแดงๆตรงช่องกลางเต็มไปหมด
ในคู่มือบอกว่า ให้กำหนดค่าเพิ่มหนึ่งค่าคือ CMAKE_BUILD_TYPE ให้เป็น Releaseตามรูปครับ
จากนั้นก็กด Configure อีกครั้ง แถบสีแดงก็จะหายไป
ให้กด Generate ถ้าเสร็จแล้ว ปิด CMake ได้
ไปที่ dos prompt แล้ว เข้าไปที่ไดเรกทอรีผลลัพธ์ เช่น C:\OpenCV231
พิมพ์ mingw32-make แล้วรอๆๆๆๆๆ นานอยู่ครับ
ความจริงถึงขั้นตอนนี้ก็เสร็จแล้วครับ แต่ถ้าอยากได้แค่ตัวหลักๆสำหรับการใช้งาน ให้ใช้ dos prompt ที่ไดเรกทอรีเดิม แล้วพิมพ์ mingw32-make install
จากนั้น จะพบว่าในไดเรกทอรีย่อย install จะมีไฟล์ทุกอย่างพร้อมใช้งานครับ ซึ่งหลักๆก็จะมีไดเรกทอรีย่อย bin, lib, include,doc ซึ่งสามารถแจกจ่ายให้คนที่ไม่ต้องการคอมไพล์เองก็ได้
สมมติว่าเราจะสำเนาทุกๆไฟล์ในไดเรกทอรี install นี้ไปไว้ยัง C:\OpenCV เพื่อใช้งานต่อไปนะครับ
เป็นอันว่าเราได้คอมไพล์และลิงก์ OpenCV สำเร็จ ได้ library ต่างๆที่เราต้องการแล้วครับ
เอกสารอ้างอิง
http://opencv.willowgarage.com/wiki/MinGW
http://www.cmake.org
http://www.mingw.org/
OpenCV 2.31 & Mingw (Dev-C++) Part 1
OpenCV เวอร์ชันล่าสุด 2.31 ออกมาได้ระยะหนึ่งแล้วครับ
พอดีว่าผมกลับมาอยากใช้ OpenCV บนวินโดวส์ในช่วงนี้ โดยเฉพาะตัวล่าสุดที่มีฟังก์ชันใหม่ๆที่น่าสนใจ
เลยไปดาวน์โหลดจาก http://sourceforge.net/projects/opencvlibrary/files/opencv-win/2.3.1/ มาครับ
ได้ไฟล์ OpenCV-2.3.1-win-superpack.exe มาครับ มีทุกอย่างเลย ทั้ง source code และ binary (ที่คอมไพล์มาแล้ว) แตกไฟล์ได้ 1 GB :(
ในส่วนของ binary มีทั้งของ x86(32 bits) x64 มีทั้งสำหรับ mingw VC9 VC10
สำหรับผมวางแผนว่าจะใช้กับ mingw (Dev-C++) ก็เลยยิ้ม เพราะมีให้ใช้
กำหนดไดเรกทอรีต่างๆเสร็จ คอมไฟล์ผ่าน ดีใจๆ
แต่ ลองรันโค้ดตามตัวอย่างครับ ไม่ผ่าน เด้งออกตลอด
คราวนี้งงเลยครับ นั่งเสิร์ชหาในเน็ตกระหน่ำ ลองกับโปรแกรมตัวอื่นๆก็ยังไม่เวิร์ค
จะเปลี่ยนไปใช้ Visual C++ ก็ยังไม่อยากใช้
หากันอยู่หนึ่่งวันเต็มๆ สุดท้ายตัดสินใจคอมไพล์ source code ใหม่เลยก็แล้วกัน
พอดีว่าผมกลับมาอยากใช้ OpenCV บนวินโดวส์ในช่วงนี้ โดยเฉพาะตัวล่าสุดที่มีฟังก์ชันใหม่ๆที่น่าสนใจ
เลยไปดาวน์โหลดจาก http://sourceforge.net/projects/opencvlibrary/files/opencv-win/2.3.1/ มาครับ
ได้ไฟล์ OpenCV-2.3.1-win-superpack.exe มาครับ มีทุกอย่างเลย ทั้ง source code และ binary (ที่คอมไพล์มาแล้ว) แตกไฟล์ได้ 1 GB :(
ในส่วนของ binary มีทั้งของ x86(32 bits) x64 มีทั้งสำหรับ mingw VC9 VC10
สำหรับผมวางแผนว่าจะใช้กับ mingw (Dev-C++) ก็เลยยิ้ม เพราะมีให้ใช้
กำหนดไดเรกทอรีต่างๆเสร็จ คอมไฟล์ผ่าน ดีใจๆ
แต่ ลองรันโค้ดตามตัวอย่างครับ ไม่ผ่าน เด้งออกตลอด
คราวนี้งงเลยครับ นั่งเสิร์ชหาในเน็ตกระหน่ำ ลองกับโปรแกรมตัวอื่นๆก็ยังไม่เวิร์ค
จะเปลี่ยนไปใช้ Visual C++ ก็ยังไม่อยากใช้
หากันอยู่หนึ่่งวันเต็มๆ สุดท้ายตัดสินใจคอมไพล์ source code ใหม่เลยก็แล้วกัน
Tuesday, March 22, 2011
ImageJ 7: Pixel processing using Lookup Table
ในการประมวลผลทีละพิกเซล บางครั้งถ้าการประมวลผลซับซ้อนและมีพิกเซลจำนวนมาก ก็จะต้องเกิด computing cost + time cost ที่สูง
เพื่อลดปัญหาดังกล่าว สำหรับรูปสีเทาที่มีแค่ 256 สี นิยมใช้ Lookup Table ที่เป็นตารางเก็บค่าผลลัพธ์ของพิกเซลที่มีค่า 0-255 ไว้ จากนั้นจึงนำมาเปรียบเทียบกับรูป ซึ่งจะทำให้การประมวลผลได้เร็วขึ้น (อาจจะใช้หน่วยความจำเพิ่มขึ้นเล็กน้อย)
ตัวอย่างเช่น รูปสีเทารูปหนึ่งมีขนาด 512x512 พิกเซล แต่ละพิกเซลต้องการการประมวลผล 2 วินาที เวลาที่ใช้ทั้งหมดคือ 2^19 วินาที
ถ้าใช้ Lookup Table ขนาด 1x256 จะใช้เวลาในการประมวลผล 2^9 วินาที บวกกับการให้ค่าจากตารางอีกนิดหน่อย รวมแล้วก็จะน้อยกว่าวิธีปกติมาก
ลองสังเกตการเพิ่ม contrast ของรูป 50% และหา inverse โดยใช้วิธีปกติ และ ใช้ Lookup Table
1. วิธีปกติ
2. Lookup Table
ซึ่งจากการเปรียบเทียบเมื่อทดสอบบนรูปสีเทาขนาด 256x256 พิกเซล พบว่า ใช้เวลาในการประมวลผล = 0.011 และ 0.006 วินาทีตามลำดับ
หมายเหตุ เราสามารถใช้คำสั่งเพื่อให้ค่าจาก Lookup Table โดยตรงแทนลูปที่สอง โดยใช้คำสั่ง ip.applyTable(LUT); ได้
เพื่อลดปัญหาดังกล่าว สำหรับรูปสีเทาที่มีแค่ 256 สี นิยมใช้ Lookup Table ที่เป็นตารางเก็บค่าผลลัพธ์ของพิกเซลที่มีค่า 0-255 ไว้ จากนั้นจึงนำมาเปรียบเทียบกับรูป ซึ่งจะทำให้การประมวลผลได้เร็วขึ้น (อาจจะใช้หน่วยความจำเพิ่มขึ้นเล็กน้อย)
ตัวอย่างเช่น รูปสีเทารูปหนึ่งมีขนาด 512x512 พิกเซล แต่ละพิกเซลต้องการการประมวลผล 2 วินาที เวลาที่ใช้ทั้งหมดคือ 2^19 วินาที
ถ้าใช้ Lookup Table ขนาด 1x256 จะใช้เวลาในการประมวลผล 2^9 วินาที บวกกับการให้ค่าจากตารางอีกนิดหน่อย รวมแล้วก็จะน้อยกว่าวิธีปกติมาก
ลองสังเกตการเพิ่ม contrast ของรูป 50% และหา inverse โดยใช้วิธีปกติ และ ใช้ Lookup Table
1. วิธีปกติ
public void run(ImageProcessor ip){ int MN = ip.getPixelCount(); for(int p=0;p<MN;p++){ int v = ip.get(p); v = (int) Math.round(1.5*v); //50% increase of contrast if(v>255) //clamp max v = 255; v = 255-v; //inverse ip.set(p,v); } }
2. Lookup Table
public void run(ImageProcessor ip){ int K = 256; //gray level int[] LUT = new int[K]; //lookup table for(int i=0;i<K;i++){ int v = (int) Math.round(i*1.5); //increase contrast 50% if(v>255) //clamp max v = 255; v = 255-v; //inverse LUT[i] = v; } //look for value in lookup table int MN = ip.getPixelCount(); for(int p=0;p<MN;p++){ ip.set(p,LUT[ip.get(p)]); } //ip.applyTable(LUT); is better for this loop }
ซึ่งจากการเปรียบเทียบเมื่อทดสอบบนรูปสีเทาขนาด 256x256 พิกเซล พบว่า ใช้เวลาในการประมวลผล = 0.011 และ 0.006 วินาทีตามลำดับ
หมายเหตุ เราสามารถใช้คำสั่งเพื่อให้ค่าจาก Lookup Table โดยตรงแทนลูปที่สอง โดยใช้คำสั่ง ip.applyTable(LUT); ได้
Monday, March 21, 2011
ImageJ 6: Basic Gray-level Thresholding
การทำ Gray-level Thresholding ก็คือการเปลี่ยนจากรูปสีเทาให้เป็นรูปขาวดำ โดยอาศัยค่า Threshold เป็นตัวแบ่ง ด้วยหลักการง่ายๆ เช่น
ถ้าเลือกค่า Threshold เป็น 100 ดังนั้น ทุกพิกเซลที่มีค่าน้อยกว่าหรือเท่ากับ 100 ให้มีค่าเป็น 0 (ดำ) นอกนั้นให้มีค่าเป็น 255 (ขาว) เช่น
หลังจากทำ Thresholding
สำหรับ ImageJ มีฟังก์ชันในคลาส ImageProcessor ชื่อ void threshold(int) เพื่อทำหน้าที่นี้
ลองดูตัวอย่างครับ
ถ้าเลือกค่า Threshold เป็น 100 ดังนั้น ทุกพิกเซลที่มีค่าน้อยกว่าหรือเท่ากับ 100 ให้มีค่าเป็น 0 (ดำ) นอกนั้นให้มีค่าเป็น 255 (ขาว) เช่น
หลังจากทำ Thresholding
สำหรับ ImageJ มีฟังก์ชันในคลาส ImageProcessor ชื่อ void threshold(int) เพื่อทำหน้าที่นี้
ลองดูตัวอย่างครับ
import ij.IJ; import ij.ImagePlus; import ij.plugin.filter.PlugInFilter; import ij.process.ImageProcessor; public class My_Threshold implements PlugInFilter { public int setup(String arg, ImagePlus imp) { return DOES_8G; //accept only 8-bit grayscale image } public void run(ImageProcessor ip) { int th = (int) IJ.getNumber("Threshold value (0-255)",100); if(th==IJ.CANCELED) //if click cancel return; if((th<0) || (th>255)){ IJ.error("Error","0-255 only"); return; } ip.threshold(th); //thresholding /* int MN = ip.getPixelCount(); for(int p=0;p<MN;p++){ if(ip.get(p)<=th) ip.set(p,0); else ip.set(p,255); } */ } }
ImageJ 5: Contrast and Brightness
ในหัวข้อนี้เราจะลองปรับค่า contrast และ brightness ของภาพ
contrast คือ ความแตกต่างของเฉดสี เช่น
contrast สูง คือ เฉดสีต่างกันมาก
contrast ต่ำ คือ เฉดสีใกล้เคียงกัน
brightness คือ ความมืดหรือความสว่างของภาพ เช่น
brightness สูง คือ ภาพสว่าง
brightness ต่ำ คือ ภาพมืด
นั่นคือ
การปรับ contrast คือ การคูณหรือหารค่าพิกเซล
การปรับ brightness คือ การบวกหรือลบค่าพิกเซล
ลองพิจารณารหัสโปรแกรมในการปรับค่า contrast ดังนี้
ส่วนการปรับค่า brightness ก็สามารถทำได้ดังนี้
contrast คือ ความแตกต่างของเฉดสี เช่น
contrast สูง คือ เฉดสีต่างกันมาก
contrast ต่ำ คือ เฉดสีใกล้เคียงกัน
brightness คือ ความมืดหรือความสว่างของภาพ เช่น
brightness สูง คือ ภาพสว่าง
brightness ต่ำ คือ ภาพมืด
นั่นคือ
การปรับ contrast คือ การคูณหรือหารค่าพิกเซล
การปรับ brightness คือ การบวกหรือลบค่าพิกเซล
ลองพิจารณารหัสโปรแกรมในการปรับค่า contrast ดังนี้
//Plugin to change contrast import ij.IJ; import ij.ImagePlus; import ij.plugin.filter.PlugInFilter; import ij.process.ImageProcessor; public class My_Contrast implements PlugInFilter { public int setup(String arg, ImagePlus imp) { return DOES_8G; //accept only 8-bit grayscale image } public void run(ImageProcessor ip) { double ad = IJ.getNumber("Adjust percent of contrast",50); if(ad==IJ.CANCELED) //if click cancel return; int MN = ip.getPixelCount(); for(int p=0;p<MN;p++) { //contrast int v = (int)(ip.get(p)*(1+ad/100)); if(v>255) //clamp max value v = 255; if(v<0) //clamp min value v = 0; ip.set(p,v); } } }
ส่วนการปรับค่า brightness ก็สามารถทำได้ดังนี้
public void run(ImageProcessor ip) { double ad = IJ.getNumber("Adjust value of brigtness",50); if(ad==IJ.CANCELED) //if click cancel return; int MN = ip.getPixelCount(); for(int p=0;p<MN;p++) { //brightness int v = (int) (ip.get(p) + ad); if(v>255) //clamp max value v = 255; if(v<0) //clamp min value v = 0; ip.set(p,v); } }
Sunday, March 20, 2011
ImageJ 4: Message
ในการเขียนโปรแกรม สิ่งที่หลีกเลี่ยงไม่ได้คือการตรวจสอบความผิดพลาด ซึ่งวิธีการหนึ่งคือการ watch ค่าตัวแปร หรือแสดงค่าตัวแปรที่กำลังสนใจอยู่
ใน ImageJ เรามีคำสั่งสำหรับการแสดงข้อความหรือตัวแปร เช่น
IJ.log(String) ตัวอย่างเช่น
ซึ่งจะได้ผลลัพธ์ คือ
หากต้องการแสดงข้อความที่ status bar ก็จะสามารถใช้คำสั่ง
IJ.showStatus(String) เช่น
ซึ่งจะได้ผลลัพธ์
ImageJ ยังสนับสนุนการแสดงข้อความเป็น dialog box ในหลายรูปแบบ เช่น
-แสดงข้อความผิดพลาด ใช้คำสั่ง IJ.error(String Title, String ErrorMessage); เช่น
IJ.error("Error","Need user input");
จะได้ผลลัพธ์เป็น
หากต้องการให้ผู้ใช้ป้อนค่าตัวอักษร สามารถใช้คำสั่ง String IJ.getString(String Title, String DefaultString) เช่น
String name = IJ.getString("Please enter your name: ","Your name here");
ก็จะเกิดกล่องข้อความดังนี้
จากนั้นเราก็สามารถใช้ตัวแปรที่รับมาได้ เช่น ใช้แสดงในกล่องข้อความใหม่ โดยใช้คำสั่ง IJ.showMessage(String Title, String message) เช่น
IJ.showMessage("Finished",name+" is running ImageJ");
จะได้ผลลัพธ์คือ
ใน ImageJ เรามีคำสั่งสำหรับการแสดงข้อความหรือตัวแปร เช่น
IJ.log(String) ตัวอย่างเช่น
public void run(String arg) { // log IJ.log("Use log method to debug variable"); int a = 99; IJ.log("a = "+a); }
ซึ่งจะได้ผลลัพธ์ คือ
หากต้องการแสดงข้อความที่ status bar ก็จะสามารถใช้คำสั่ง
IJ.showStatus(String) เช่น
public void run(String arg) { // show text in status bar IJ.showStatus("Message at status bar"); }
ซึ่งจะได้ผลลัพธ์
ImageJ ยังสนับสนุนการแสดงข้อความเป็น dialog box ในหลายรูปแบบ เช่น
-แสดงข้อความผิดพลาด ใช้คำสั่ง IJ.error(String Title, String ErrorMessage); เช่น
IJ.error("Error","Need user input");
จะได้ผลลัพธ์เป็น
หากต้องการให้ผู้ใช้ป้อนค่าตัวอักษร สามารถใช้คำสั่ง String IJ.getString(String Title, String DefaultString) เช่น
String name = IJ.getString("Please enter your name: ","Your name here");
ก็จะเกิดกล่องข้อความดังนี้
จากนั้นเราก็สามารถใช้ตัวแปรที่รับมาได้ เช่น ใช้แสดงในกล่องข้อความใหม่ โดยใช้คำสั่ง IJ.showMessage(String Title, String message) เช่น
IJ.showMessage("Finished",name+" is running ImageJ");
จะได้ผลลัพธ์คือ
Sunday, March 13, 2011
ImageJ 3: Histogram
ตัวอย่างนี้จะเป็นการลองสร้าง Histogram ของรูปสีเทารูปนี้ครับ
ผลลัพธ์ที่อยากได้คือ Histogram ประมาณนี้
ImageJ มีฟังก์ชันในการคำนวณ Histogram ให้คือ getHistogram(); ซึ่งจะใช้หรือเขียนฟังก์ชันเองก็ได้ตามตัวอย่างต่อไปนี้ครับ
ส่วนการแสดงผล จำเป็นต้องสร้างหน้าต่างขึ้นมาใหม่ แล้ววาดจุดสีดำลงไปครับ ซึ่งโค้ดทั้งหมดต่อไปนี้ ยังอยู่ใน method run() ครับ
ผลลัพธ์ที่อยากได้คือ Histogram ประมาณนี้
ImageJ มีฟังก์ชันในการคำนวณ Histogram ให้คือ getHistogram(); ซึ่งจะใช้หรือเขียนฟังก์ชันเองก็ได้ตามตัวอย่างต่อไปนี้ครับ
//Plugin to compute histogram import ij.ImagePlus; import ij.plugin.filter.PlugInFilter; import ij.process.ImageProcessor; import ij.process.ByteProcessor; public class My_Histogram implements PlugInFilter { public int setup(String arg, ImagePlus im) { return DOES_8G + NO_CHANGES; //accept only 8-bit grayscale image } public void run(ImageProcessor ip) { int[] H = new int[256]; /* int MN = ip.getPixelCount(); for(int p=0;p<MN;p++) { int v = ip.get(p); H[v] += 1; } */ H = ip.getHistogram(); //built-in method } }
ส่วนการแสดงผล จำเป็นต้องสร้างหน้าต่างขึ้นมาใหม่ แล้ววาดจุดสีดำลงไปครับ ซึ่งโค้ดทั้งหมดต่อไปนี้ ยังอยู่ใน method run() ครับ
//find max of histogram bin int max=H[0]; for(int b=1;b<256;b++) { if(max<H[b]) max=H[b]; } //create the image of histogram //prepare blank area of w=256 h=100 ImageProcessor histip = new ByteProcessor(256,100); histip.setValue(255) ; // white = 255 histip.fill(); // clear this image //draw histogram bins for(int b=0;b<256;b++) { int hh = (int)(100.0*H[b]/max); //normalized bin's height for(int r=0;r<hh;r++) { histip.set(b,99-r,0); //plot black points } } // display the histogram image: ImagePlus histim = new ImagePlus("Histogram", histip); histim.show();
ImageJ 2: Invert an image
เรายังอยู่ที่การ invert image ครับ แต่จะลองใช้เทคนิคในการเข้าถึงพิกเซลต่างๆกัน ลองดูตัวอย่างโคัดครับ ในที่นี้มีอย่างน้อย 4 วิธี ดูรายละเอียดตามที่หมายเหตุได้ครับ
ตามทฤษฎีแล้วแต่ละวิธีจะให้ความเร็วต่างกัน ซึ่งจะเห็นชัดขึ้นเมื่อรูปมีขนาดใหญ่ขึ้นครับ
//Plugin to invert a grayscale image import ij.ImagePlus; import ij.plugin.filter.PlugInFilter; import ij.process.ImageProcessor; import ij.process.ByteProcessor; // _ in the class name = plugin public class My_Invert implements PlugInFilter { public int setup(String arg, ImagePlus im) { return DOES_8G; //accept only 8-bit grayscale image } public void run(ImageProcessor ip) { //1. use getPixel, putPixel to access each pixel //they check image boundary+invoke function call, slowest int w = ip.getWidth(); int h = ip.getHeight(); //for each pixel for(int x=0;x<w;x++) for(int y=0;y<h;y++) ip.putPixel(x,y,255-ip.getPixel(x,y)); /* //2. use get,set which do not check boundary //still invoke function, faster int w = ip.getWidth(); int h = ip.getHeight(); //for each pixel for(int x=0;x<w;x++) for(int y=0;y<h;y++) ip.set(x,y,255-ip.get(x,y)); */ /* //3. use get,set with 1d indices //only 1 loop, invoke function, more faster int MN = ip.getPixelCount(); //total pixels for(int p=0;p<MN;p++) ip.set(p, 255-ip.get(p)); */ /* //4. use getPixels to directly process pixel array //direct access, no function invocation //fastest but more memory need //check if ip is ByteProcessor (0-255) if(!(ip instanceof ByteProcessor)) return; if(!(ip.getPixels() instanceof byte[])) return; //dump to array but by reference!!! byte[] pixels = (byte[]) ip.getPixels(); int MN = ip.getPixelCount(); for(int p=0;p<MN;p++) { int v = 0xFF & pixels[p]; //convert to integer v = 255-v; pixels[p] = (byte) (0xFF & v); //to byte } */ } }
ตามทฤษฎีแล้วแต่ละวิธีจะให้ความเร็วต่างกัน ซึ่งจะเห็นชัดขึ้นเมื่อรูปมีขนาดใหญ่ขึ้นครับ
ImageJ 1: Invert an image
ImageJ เป็นซอฟต์แวร์ทางด้านการประมวลผลภาพที่มีประสิทธิภาพ ไม่เสียค่าใช้จ่าย และใช้งานได้ไม่ยากครับ
ในที่นี้เราจะลองศึกษาการประยุกต์ใช้งาน โดยสร้าง plugin ซึ่งเนื้อหาโดยส่วนใหญ่ในหัวข้อนี้และถัดๆไป ผมจะอ้างอิงจากเอกสารในเว็บ http://rsbweb.nih.gov/ij/ และหนังสือ Wilhelm Burger and Mark James Burge, Digital Image Processing: An Algorithmic Introduction using Java, Springer, 2008.
ซึ่งไม่ได้มีเจตนาจะละเมิดลิขสิทธิ์ใดๆทั้งสิ้น
ถ้าพร้อมแล้ว ดาวน์โหลด ImageJ ได้ที่ http://rsbweb.nih.gov/ij/ ครับ
หลังจากคลายไฟล์ที่ดาวน์โหลดมาแ้ล้ว เมื่อเรียกใช้งานก็จะเห็นหน้าตาแบบนี้ครับ
เราจะลองสร้าง plugin เพื่อเพิ่มความสามารถในการประมวลผลภาพตามที่เราต้องการ ในครั้งนี้เราจะลอง invert รูปสีเทาครับ ที่เราเรียกว่าทำ image negative
ขั้นตอนในการสร้าง PlugIn
1. สร้างโฟลเดอร์ หรือ ไฟล์จาวา ในไดเรกทอรี ImageJ/plugins ครับ ในที่นี้เราจะสร้างโฟลเดอร์ชื่อ My และสร้างไฟล์ชื่อ My_Invert.java ไว้
โปรดสังเกตว่า เราใช้เครื่องหมาย _ เพื่อแสดงว่าไฟล์นี้เป็น plugin และจะถูกโหลดเข้าไปใน ImageJ โดยอัตโนมัติ
2. ใช้ IDE ตัวไหนก็ได้แก้ไขไฟล์จาวา ชื่อคลาสต้องตรงกับชื่อไฟล์ plugin
3. ใช้ ImageJ คอมไพล์ไฟล์นั้น
4. แ้ก้ไขไฟล์ถ้ามีข้อผิดพลาด
5. ถ้าไม่มีข้อผิดพลาดก็จะใช้ plugin นั้นได้เลยทันที
ลองมาดูโค้ดของ plugin invert กันครับ
ทดลองโหลดรูปใดๆ ขึ้นมาก่อน เช่น
จากนั้น คอมไพล์โดยใช้เมนู Plugin / Compile and Run...
ผลลัพธ์ที่ได้
จะเห็นได้ว่าเรามีการเข้าถึงและประมวลผลพิกเซลได้โดยสะดวกครับ
ในที่นี้เราจะลองศึกษาการประยุกต์ใช้งาน โดยสร้าง plugin ซึ่งเนื้อหาโดยส่วนใหญ่ในหัวข้อนี้และถัดๆไป ผมจะอ้างอิงจากเอกสารในเว็บ http://rsbweb.nih.gov/ij/ และหนังสือ Wilhelm Burger and Mark James Burge, Digital Image Processing: An Algorithmic Introduction using Java, Springer, 2008.
ซึ่งไม่ได้มีเจตนาจะละเมิดลิขสิทธิ์ใดๆทั้งสิ้น
ถ้าพร้อมแล้ว ดาวน์โหลด ImageJ ได้ที่ http://rsbweb.nih.gov/ij/ ครับ
หลังจากคลายไฟล์ที่ดาวน์โหลดมาแ้ล้ว เมื่อเรียกใช้งานก็จะเห็นหน้าตาแบบนี้ครับ
เราจะลองสร้าง plugin เพื่อเพิ่มความสามารถในการประมวลผลภาพตามที่เราต้องการ ในครั้งนี้เราจะลอง invert รูปสีเทาครับ ที่เราเรียกว่าทำ image negative
ขั้นตอนในการสร้าง PlugIn
1. สร้างโฟลเดอร์ หรือ ไฟล์จาวา ในไดเรกทอรี ImageJ/plugins ครับ ในที่นี้เราจะสร้างโฟลเดอร์ชื่อ My และสร้างไฟล์ชื่อ My_Invert.java ไว้
โปรดสังเกตว่า เราใช้เครื่องหมาย _ เพื่อแสดงว่าไฟล์นี้เป็น plugin และจะถูกโหลดเข้าไปใน ImageJ โดยอัตโนมัติ
2. ใช้ IDE ตัวไหนก็ได้แก้ไขไฟล์จาวา ชื่อคลาสต้องตรงกับชื่อไฟล์ plugin
3. ใช้ ImageJ คอมไพล์ไฟล์นั้น
4. แ้ก้ไขไฟล์ถ้ามีข้อผิดพลาด
5. ถ้าไม่มีข้อผิดพลาดก็จะใช้ plugin นั้นได้เลยทันที
ลองมาดูโค้ดของ plugin invert กันครับ
//Plugin to invert a grayscale image import ij.ImagePlus; import ij.plugin.filter.PlugInFilter; import ij.process.ImageProcessor; import ij.process.ByteProcessor; // _ in the class name = plugin public class My_Invert implements PlugInFilter { public int setup(String arg, ImagePlus im) { return DOES_8G; //accept only 8-bit grayscale image } public void run(ImageProcessor ip) { //1. use getPixel, putPixel to access each pixel //they check image boundary+invoke function call, slowest //0.015 second int w = ip.getWidth(); int h = ip.getHeight(); //for each pixel for(int x=0;x<w;x++) for(int y=0;y<h;y++) ip.putPixel(x,y,255-ip.getPixel(x,y)); } }
ทดลองโหลดรูปใดๆ ขึ้นมาก่อน เช่น
จากนั้น คอมไพล์โดยใช้เมนู Plugin / Compile and Run...
ผลลัพธ์ที่ได้
จะเห็นได้ว่าเรามีการเข้าถึงและประมวลผลพิกเซลได้โดยสะดวกครับ
Subscribe to:
Posts (Atom)