Saturday, July 3, 2010

Basic Image Loading and Display

เรามาลองทำโปรเจคเกี่ยวกับ Image processing เบื้องต้นด้วยจาวากันครับ

ก่อนอื่น เราจะออกแบบให้มีหน้าแสดงผลง่ายๆ โดยใช้เฟรม (JFrame) ครับ ซึ่งก็จะมีหน้าตาดังนี้

ซึ่งก็ใช้รหัสโปรแกรมง่ายๆ ดังนี้ครับ
package dip;

import javax.swing.JFrame;

public class Main
{
    public static void main(String[] args)
    {
        new ImageFrame();
    }
}

class ImageFrame extends JFrame
{
    public ImageFrame()
    {
        //set Frame's properties
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setTitle("Image Processing");   //frame title
        setSize(250,250);   //initial size
        setLocationRelativeTo(null); //set window to center of the screen
        setVisible(true);
    }    
}

ในขั้นตอนต่อไปก็จะทำการทดลองอ่านรูปขึ้นมา แล้วแสดงผลในหน้าต่างดังกล่าว ซึ่งจะมีขั้นตอนคือ

  1. สร้าง component เพื่อรองรับรูป อาจจะเป็น JComponent หรือ JPanel ก็ำได้
  2. อ่านรูปที่ต้องการ
  3. วาดรูปนั้นลงบน component
  4. เพิ่ม component ที่มีรูปอยู่นี้บนเฟรม
ขั้นตอนที่ 1-3 สร้าง component เพื่อรองรับรูป, อ่านรูปที่ต้องการ และวาดรูปลงบน component โดยกำหนดให้รูปอยู่ที่ d:/lena.jpg
เราจะสร้างคลาสขึ้นมาใหม่ ดังนี้

package dip;

import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JComponent;

class Canvas extends JComponent
{
    private BufferedImage img;

    public Canvas()
    {
        try
        {
            //read an image lena.jpg
            img = ImageIO.read(new File("d:/lena.jpg"));
        }
        catch(IOException e)
        {
            e.printStackTrace();
        }
    }

    public void paintComponent(Graphics g)
    {
        if(img==null)
            return;
        g.drawImage(img,0,0,null);
    }
}

ขั้นตอนที่ 4 เพิ่ม component นี้ให้เฟรม ในที่นี้เราจะแก้ไข constructor ของเฟรมหลัก ดังนี้

    public ImageFrame()
    {
        //set Frame's properties
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setTitle("Image Processing");   //frame title
        setSize(250,250);   //initial size
        setLocationRelativeTo(null); //set window to center of the screen
       //create and add image component to frame
        Canvas myCanvas = new Canvas();
        add(myCanvas);
        setVisible(true);
    } 

เมื่อรันโปรแกรม ผลลัพธ์ที่ได้ก็จะเป็นดังนี้ครับ

Tuesday, May 11, 2010

Table's backshashbox

\usepackage{slashbox}

\begin{tabular}{|l|l|l|} 
\hline
\backslashbox[2cm]{Lesson}{Date} & Monday & Tuesday\\
\hline
Stratigraphy &roomA & roomA\\
Chemistry & roomB & Lab$\alpha$\\
Physics & roomC & Lab$\beta$\\
\hline
\end{tabular}

ผลลัพธ์

ทำกรอบล้อมข้อความ กำหนดสีพื้นหลัง

วิธีง่ายๆคือ ใช้แพคเกจ
\usepackage{framed}

แล้วใช้งาน เช่น
\begin{frame}
text
\end{frame}

หรือถ้าอยากใช้แถบในแนวดิ่งแทน ก็ใช้
\begin{leftbar}
 text
\end{leftbar}

หรือถ้าต้องการกำหนดสีพื้นหลังของข้อความ
\usepackage{framed}
\usepackage{color}

\definecolor{shadecolor}{rgb}{0.9,0.8,1}
\begin{shaded}
text
\end{shaded}

Thursday, April 8, 2010

Remote Sensing Software

ปีนี้ผมมีงานวิจัยที่เกี่ยวข้องกับ Remote Sensing Image Processing ครับ เลยต้องหาซอฟต์แวร์ทางด้านนี้มาทดลองใช้งาน ตัวดังๆก็ได้แก่ ARCGIS ERDAS แต่ไม่มีตังค์ซื้อครับ เลยต้องทดลองหาของฟรีมาใช้ ที่สนใจอยู่ตอนนี้ก็มี 3 ตัวครับ คือ
  1. MultiSpec ตัวเล็ก ทำได้หลายอย่าง เสียอย่างเดียวไม่เป็น open source และไม่มีระบบ plug-in น่าจะเหมาะกับการเรียนรู้มากกว่าเอาไปใช้งานจริง
  2. Opticks น่าสนใจครับ เป็น open source ด้วย สนับสนุน plug-in (แต่มีน้อยเหลือเกิน) ดูจะขาดเครื่องมือทางด้าน clustering อยู่ :(
  3. GRASS GIS อันนี้ตัวใหญ่เลยครับ ทำได้ทั้งทางด้าน GIS+remote sensing แต่ว่าคู่มือ รายละเอียดต่างๆยังไม่ค่อยชัดเจนเท่าไหร่ เป็นความหวังอันหนึ่งเลยครับ
ตอนนี้คงต้องลองๆไปก่อนละครับ หวังว่าสุดท้ายคงไม่ต้องกลับไปใช้ matlab หรือ OpenCV นะ _/|\_

Wednesday, April 7, 2010

OpenCV 2.1 มาแล้วครับ

OpenCV 2.1 มีให้ดาวน์โหลดไปใช้งานแล้วครับ ตั้งแต่วันที่ 6 เมษายน 2553 ตามเอกสารระบุว่ามีการแก้ไข bug มากกว่า 200 รายการ สนับสนุนระบบปฏิบัติการแบบ 64 บิต และแบบอื่นๆ อีกมากมาย

ลองไปดาวน์โหลดได้ที่ http://sourceforge.net/projects/opencvlibrary/files/ ครับ

สำหรับ Windows ควรเลือกไฟล์  OpenCV-2.1.0-win32-vs2008.exe นะครับ

ขั้นตอนการติดตั้งและใช้งานกับ Dev-CPP
สำหรับเวอร์ชันนี้ผมคิดว่าสนับสนุนการใช้งานกับ Visual Studio 2008 อยู่แล้วตามชื่อไฟล์ เราลองมาติดตั้งและใช้งานกับ Dev-CPP กันดูครับ

1. uninstall OpenCV เวอร์ชันเดิมออกก่อน
2. ติดตั้งเวอร์ชันใหม่ สมมติว่าติดตั้งที่ C:\OpenCV2.1
3. เปิดโปรแกรม Dev-CPP เลือกหัวข้อ Tools/ Compiler Options
4. แก้ไขคำสั่งเพิ่มเติมในส่วนของ Linker ให้เป็น -lcv210 -lcvaux210 -lcxcore210 -lhighgui210 -lml210 ตามรูป

5. แก้ไขส่วนของแทบ Directories
5.1 หัวข้อ Binaries เพิ่ม C:\OpenCV2.1\bin

5.2 หัวข้อ libraries เพิ่ม C:\OpenCV2.1\lib

5.3 หัวข้อ C-Includes และ C++-Includes ให้เพิ่ม C:\OpenCV2.1\include\opencv

จากนั้นทดสอบโปรแกรมโหลดและแสดงผลรูปตามนี้ครับ สมมติว่ามีไฟล์รูป lena.jpg อยู่ในไดเรกทอรีเดียวกับตัวรหัสโปรแกรมนะครับ


ข้อดีในการติดตั้ง
ไม่ต้องแก้ไขไฟล์ cxoperations.hpp เหมือนในเวอร์ชัน 2.0 อีก

ปัญหาที่พบ
 ผมไม่สามารถใช้คำสั่งภายใต้ namespace cv ได้เลย เช่นคำสั่ง imread, imshow, namedWindow, waitKey ตัวคอมไพเลอร์แจ้งว่ามีข้อผิดพลาดที่ linker ไม่เจอคำสั่งเหล่านี้ ทั้งๆที่ในเวอร์ชัน 2.0 กลับทำได้ ต้องลองหาทางแก้ต่อไปครับ

Tuesday, April 6, 2010

การเปลี่ยนขนาดรูป (Resize)

เมื่อต้องการเปลี่ยนขนาดของรูป เช่น ย่อ หรือ ขยาย เราสามารถใช้คำสั่ง cvResize() ได้ ตามรูปแบบดังนี้

cvResize(imgin, imgout, type)

เมื่อ imgin imgout และ type คือรูปต้นฉบับ รูปผลลัพธ์ และ ชนิดของการทำ interpolation ตามลำดับ

สังเกตว่า คำสั่งนี้ไม่มีการระบุว่าให้เปลี่ยนขนาดของรูปเป็นเท่าใด ทั้งนี้เพราะ รูปจะถูกเปลี่ยนให้มีขนาดเท่ากับรูปผลลัพธ์นั่นเอง ดังนั้น เราจำเป็นต้องกำหนดขนาดใหม่ที่ต้องการให้กับรูปผลลัพธ์ไว้ล่วงหน้า

ชนิดของการทำ interpolation มีสี่รูปแบบคือ CV_INTER_NN (Nearest neighbor), CV_INTER_LINEAR (Bilinear), CV_INTER_AREA (Pixel area resampling) และ CV_INTER_CUBIC (Bicubic)

ลองพิจารณาัตัวอย่างต่อไปนี้

ผลลัพธ์

แปลงภาพสีเป็นภาพขาวดำ 2 (Color to grayscale image)

บทความก่อนหน้านี้แสดงการแปลงภาพสีให้เป็นภาพขาวดำ โดยหาค่าเฉลี่ยของพิกเซลสีในแต่ละ channel

OpenCV ได้เตรียมคำสั่งสำหรับการเปลี่ยนแปลง channel ของสีไว้ให้ใช้งานได้ง่ายยิ่งขึ้น ด้วยคำสั่ง cvCvtColor เช่น
    cvCvtColor(img, out, CV_BGR2GRAY);
คือการเปลี่ยนรูป img ให้เป็น out โดยมีการเปลี่ยนจากรูปสี (BGR) เป็น grayscale (CV_BGR2GRAY)

คำสั่งนี้ยังสามารถใช้เปลี่ยนรูปแบบ channel ของสี เช่น จาก RGB เป็น HLS, HSV, YCrCb หรืออื่นๆได้ด้วย

พิจารณารหัสโปรแกรมดังต่อไปนี้


ผลลัพธ์

Monday, April 5, 2010

แปลงภาพสีเป็นภาพขาวดำ (Color to grayscale image)

หากเรามีภาพสี และต้องการเปลี่ยนให้เป็นภาพขาวดำ จะทำได้อย่างไร

หลักการง่ายๆก็คือ หาค่าเฉลี่ยของแม่สีสามสี คือ น้ำเงิน เขียวและแดง นั่นคือ
G = (B+G+R)/3
อย่างไรก็ตาม เราพบว่าตาของเราตอบสนองต่อแม่สีทั้งสามต่างกัน จึงมีผู้เสนอให้ใช้สมการต่อไปนี้แทน
G = 0.114B + 0.587G + 0.299R

หากเราต้องการเขียนโปรแกรมใน OpenCV เพื่อตอบโจทย์นี้ เราต้อง
-สร้างรูปเปล่าที่เป็น grayscale ขึ้นเพื่อเก็บผลลัพธ์
-คำนวณผลรวมพิกเซลจากรูปสี แล้วบันทึกลงในพิกเซลของรูป grayscale ณ ตำแหน่งเดียวกัน

ตัวอย่างรหัสโปรแกรมจะเป็นดังนี้


ผลลัพธ์

จากโปรแกรมข้างต้น มีข้อสังเกตดังนี้

1. เราสร้างรูป grayscale เปล่าๆ ด้วยคำสั่ง
IplImage* out = cvCreateImage(cvGetSize(img), IPL_DEPTH_8U,1);

จากพารามิเตอร์ของคำสั่ง cvCreateImage จะอธิบายได้ว่า เราต้องการสร้างรูปที่มีขนาดเท่ากับรูปต้นฉบับ (cvGetSize(img)) ให้เป็นแบบ unsigned 8 บิต (IPL_DEPTH_8U) และมีเพียง 1 channel

2. เราใช้พอยน์เตอร์สองตัว ตัวแรกชี้ที่รูปต้นฉบับ อีกตัวชี้ที่รูปผลลัพธ์

3. OpenCV มีการเรียง channel เป็นแบบ blue green red

4. เราหาผลรวมของแต่ละสี แล้วใช้พอยน์เตอร์เปลี่ยนค่าของรูป grayscale

5. หากบันทึกรูปผลลัพธ์นี้ จะได้รูปที่เป็น 8 บิต grayscale ด้วย

บันทึกไฟล์ภาพ

เราทราบดีแล้วว่า การอ่านไฟล์ภาพจะใช้คำสั่้ง
cvLoadImage()

ตรงกันข้าม หากเราต้องการบันทึกภาพเป็นไฟล์ เราจะใช้คำสั่ง
cvSaveImage() ซึ่งมีรูปแบบคือ
int cvSaveImage(const char* filename, const CvArr* image)

เช่น  หากเรามีรูปในหน่วยความจำที่เก็บในตัวแปร imgout และต้องการบันทึกเป็นไฟล์ชื่อ lenaNegative.jpg เราจะใช้คำสั่งได้ว่า
    if(!cvSaveImage("lenaNegative.jpg",imgout))
        cout<<"Could not save the image";

สำหรับชนิดของรูปที่จะอ่านและบันทึกได้ใน OpenCV จะมีดังนี้

  • Windows bitmaps - BMP, DIB
  • JPEG files - JPEG, JPG, JPE
  • Portable Network Graphics - PNG
  • Portable image format - PBM, PGM, PPM
  • Sun rasters - SR, RAS
  • TIFF files - TIFF, TIF

Sunday, April 4, 2010

Image Negative

ในบทความก่อนหน้านี้ เราพูดถึงการหาค่า negative ของภาพแบบ grayscale ไปแล้ว ในบทความนี้ จะกล่าวถึงการหาคำนวณ image negative ของภาพใดๆรวมถึงภาพสี โดยการประมวลผลทีละพิกเซล

ก่อนที่จะทำการคำนวณ เราจะต้องพิจารณาถึงลักษณะการเก็บค่าพิกเซลในหน่วยความจำเสียก่อน ซึ่งโดยทั่วไปแล้ว ภาพจะประกอบด้วยเฟรมสามเฟรมซ้อนกัน นั่นคือเฟรมของสีแดง เขียว และน้ำเงิน เราอาจเรียกเฟรมดังกล่าวว่า channel (ใน OpenCV จะมีการเรียง channel เป็นแบบ น้ำเงิน เขียว แดง หรือ BGR)

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


OpenCV มีการเก็บข้อมูลรูปในลักษณะที่ไม่ใช่อาร์เรย์สามมิติ แต่เป็นลักษณะเป็นแถวเรียงกันไป (อาจคิดได้ว่าเป็นเวกเตอร์) ดังนี้

นั่นคือ มีการเก็บพิกเซลของแต่ละสี (channel) เรียงต่อกันไป จากซ้ายไปขวาและจากบนลงล่าง

ดังนั้น หากต้องการเข้าถึงแต่ละพิกเซล จะได้ว่าตำแหน่งของพิกเซลใดๆ (x) จะมีค่าเป็น
x = (r*widthStep) + (c*nchannel) + k;
เมื่อ r และ c คือตำแหน่งของแถวและคอลัมน์ของภาพ nchannel คือ จำนวนของ channel ทั้งหมด (ในที่นี้เป็น 3) และ k คือค่าของ channel ที่กำลังสนใจ (มีค่า 0-2)

หลักการนี้สามารถนำไปใช้ในการคำนวณ image negative ได้ดังรหัสโปรแกรมต่อไปนี้



ให้สังเกตในส่วนของการประมวลผลว่ามีการใช้ถึงสามลูปซ้อนกัน นั่นคือ แถว คอลัมน์ และ channel

ผลลัพธ์

Friday, April 2, 2010

Clone รูป + หน้าต่างใหม่

ในตัวอย่างที่แล้วเราทำการหาค่า negative ของรูป ซึ่งแสดงผลลัพธ์เพียงอย่างเดียว หากเราต้องการแสดงทั้งรูปตั้งต้นและผลลัพธ์พร้อมๆกันเพื่อให้ง่ายต่อการเปรียบเทียบ เราจะต้องเพิ่มสองขั้นตอนต่อไปนี้คือ
  1. สำเนารูปภาพไว้ (clone) เพื่อให้รูปต้นฉบับยังคงเดิม
  2. สร้างหน้าต่างเพิ่มอีกหนึ่งหน้าต่างเพื่อรองรับรูปที่เพิ่มขึ้น
การสำเนารูปภาพ เราจะใช้คำสั่ง cvCloneImage ซึ่งมีรูปแบบดังนี้
IplImage* cvCloneImage(const IplImage* image)
เช่น
IplImage* imgout = cvCloneImage(img);

ส่วนการสร้างหน้าต่างใหม่ สามารถใช้คำสั่ง
cvNamedWindow
เหมือนตัวอย่างที่แล้วมา

ลองพิจารณาการปรับปรุงรหัสโปรแกรมเพื่อแสดงผลภาพ negative ใหม่ ดังต่อไปนี้


ผลลัพธ์

Thursday, April 1, 2010

Grayscale Image Negative

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

หากค่าพิกเซลเป็น 0 จะกลายเป็น 255
หากค่าพิกเซลเป็น 255 จะกลายเป็น 0
หากค่าพิกเซลเป็น 100 จะกลายเป็น 155

ลองพิจารณารหัสโปรแกรมดังต่อไปนี้


ผลลัพธ์


คำอธิบาย
ในตัวอย่างนี้ เราต้องการโหลดรูปในรูปแบบ grayscale ดังนั้นในคำสั่ง cvLoadImage เราจึงใช้พารามิเตอร์เพิ่มคือ CV_LOAD_IMAGE_GRAYSCALE เพื่อบังคับให้รูปที่ได้เป็นแบบเฉดสีเทาเท่านั้น

โดยทั่วไปแล้ว คำสั่ง cvLoadImage จะมีพารามิเตอร์ที่เป้นไปได้สามค่าคือ
  • CV_LOAD_IMAGE_COLOR the loaded image is forced to be a 3-channel color image
  • CV_LOAD_IMAGE_GRAYSCALE the loaded image is forced to be grayscale
  • CV_LOAD_IMAGE_UNCHANGED the loaded image will be loaded as is.
จากนั้นเรามีการตรวจสอบว่าสามารถโหลดรูปได้หรือไม่ โดย


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

ดังนั้น ในการจัดการแต่ละพิกเซลของรูปให้เป็นค่า negative เราจึงจะใช้รหัสโปรแกรม


เมื่อ ptr คือพอยน์เตอร์ที่ชี้ไปยังแถวแรกของรูป และตำแหน่งพิกเซลใดๆในรูป สามารถคำนวณได้จาก
rc = (r*img->widthStep) + c;
เมื่อ r และ c คือแถวและหลักของรูปตามลำดับ

ที่มา:
1. http://opencv.willowgarage.com/documentation/c/reading_and_writing_images_and_video.html#cvLoadImage
2. http://www.cs.iit.edu/~agam/cs512/lect-notes/opencv-intro/index.html

โครงสร้างของรูปใน OpenCV

รูปใน OpenCV จะถูกเก็บด้วยตัวแปรโครงสร้างชื่อ IplImage ซึ่งมีรายละเีอียดดังนี้

typedef struct _IplImage
{
int nSize;
int ID;
int nChannels; //ชั้นของสี อยู่ระหว่าง 1-4 (สีเทา แดง เขียว น้ำเงิน อัลฟ่า)
int alphaChannel;
int depth; //ความลึกของบิต (bit depth) ตั้งแต่ 8-64 บิต มีชื่อเฉพาะ
char colorModel[4];
char channelSeq[4];
int dataOrder;
int origin;
int align;
int width; //ความกว้างของภาพ
int height; //ความสูงของภาพ
struct _IplROI *roi;
struct _IplImage *maskROI;
void *imageId;
struct _IplTileInfo *tileInfo;
int imageSize; //ขนาดของรูปในหน่วยไบต์ = height*widthStep
char *imageData; //พอยน์เตอร์ที่ชี้ไปยังแถวแรกของภาพ
int widthStep; //จำนวนไบต์ระหว่างสองพิกเซลที่อยู่ในคอลัมน์เดียวกัน แต่คนละแถว
int BorderMode[4];
int BorderConst[4];
char *imageDataOrigin;
}
IplImage;

รายละเอียดเพิ่มเติมสามารถหาดูได้ที่
http://opencv.willowgarage.com/documentation/c/basic_structures.html#index-714
http://www.cs.iit.edu/~agam/cs512/lect-notes/opencv-intro/index.html

ซึ่งทำให้เราจินตนาการได้ว่า การเก็บรูปใน OpenCV จะไม่ใช่เป็นแบบเมตริกซ์สองมิติธรรมดา แต่จะเป็นดังรูปต่อไปนี้ (สมมติว่าเป็นรูป grayscale)
นั่นคือ รูปใน OpenCV จะถูกเก็บในหน่วยความจำเรียงต่อๆกันไปเหมือนอาร์เรย์ 1 มิติหรือเวกเตอร์
พิกเซลที่ (0,0) ก็จะเทียบได้กับตำแหน่งที่ 0
พิกเซลที่ (0,1) ก็จะเทียบได้กับตำแหน่งที่ 1
พิกเซลที่ (1,0) ก็จะเทียบได้กับตำแหน่งที่ widthStep
พิกเซลที่ (1,1) ก็จะเทียบได้กับตำแหน่งที่ widthStep+1
พิกเซลที่ (2,1) ก็จะเทียบได้กับตำแหน่งที่ 2*widthStep
.....

พิกเซลที่ (r,c) ก็จะเทียบได้กับตำแหน่งที่ r*widthStep+c นั่นเอง

ดังนั้น หากต้องการใช้ลูปเพื่อประมวลผลพิกเซล จะสามารถใช้รหัสดังต่อไปนี้ได้


ลองดูตัวอย่างการใช้งานในบทความถัดไปได้ครับ

Tuesday, March 30, 2010

โปรแกรมแรกกับ OpenCV

ทดลองโหลดรูปและแสดงผลด้วย OpenCV ตามโค้ดต่อไปนี้ครับ  สมมติว่าเรามีรูปชื่อ lena.jpg ในไดเรกทอรีเดียวกับโค้ดนี้ครับ



ผลลัพธ์ที่ได้

แสดงรหัสโปรแกรมบนบล็อก (Syntax Highlight on Blog)

สำหรับผู้ที่ใช้บล็อก เช่น blogspot และต้องการแสดงผลรหัสโปรแกรมให้สวยงาม สามารถทำได้ดังนี้ครับ
วิธีแรก
ใช้บริการของ http://hilite.me/ แค่แปะโค้ดของเรา เลือกภาษาที่ต้องการ แล้วก๊อป HTML โค้ดมาแปะ เรียบร้อยสวยงามครับ เช่น จากเดิม
#include<iostream>
using namespace std;

int main()
{
    cout<<"Hello World!";
    system("PAUSE");
    return 0;
}

เป็น
#include<iostream>
using namespace std;

int main()
{
    cout<<"Hello World!";
    system("PAUSE");
    return 0;
}


วิธีที่สอง
ใช้ Syntax highlighter
1. แก้ไข template ของบล็อก โดยเพิ่มคำสั่งต่อไปนี้ ในส่วนของ head แท็ก (ต้นๆ เลยก็ดีครับ)
 <link href='http://alexgorbatchev.com/pub/sh/current/styles/shCore.css' rel='stylesheet' type='text/css'/>
<link href='http://alexgorbatchev.com/pub/sh/current/styles/shThemeDefault.css' rel='stylesheet' type='text/css'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shCore.js' type='text/javascript'></script>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushCpp.js' type='text/javascript'></script>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushCSharp.js' type='text/javascript'></script>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushCss.js' type='text/javascript'></script>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushJava.js' type='text/javascript'></script>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushJScript.js' type='text/javascript'></script>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushPhp.js' type='text/javascript'></script>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushPython.js' type='text/javascript'></script>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushRuby.js' type='text/javascript'></script>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushSql.js' type='text/javascript'></script>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushVb.js' type='text/javascript'></script>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushXml.js' type='text/javascript'></script>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushPerl.js' type='text/javascript'></script>
<script language='javascript'>
SyntaxHighlighter.config.bloggerMode = true;
SyntaxHighlighter.config.clipboardSwf = 'http://alexgorbatchev.com/pub/sh/current/scripts/clipboard.swf';
SyntaxHighlighter.all();
</script>
ให้เลือกเฉพาะภาษาโปรแกรมที่ต้องการ hilight ครับ ส่วนที่ไม่ต้องการก็ลบออกได้

2. จากนั้น ในส่วนของเนื้อหาที่ต้องการ hilight ก็เข้าไปแก้ไข html โดยใช้คำสั่งต่อไปนี้
<script type="syntaxhighlighter" class="brush: cpp"><![CDATA[
รหัสโปรแกรม
]]></script>
ให้สังเกตว่าในที่นี้ใช้ภาษาซีพลัสพลัสครับ

>
ปล. อย่าลืม เปิดจาวาสคริปต์ให้เว็บ http://alexgorbatchev.com และเว็บ http://amazonaws.com ด้วยนะครับ

ที่มา:
1. http://alexgorbatchev.com/wiki/SyntaxHighlighter
2. http://blog.cartercole.com/2009/10/awesome-syntax-highlighting-made-easy.html
3. http://geektalkin.blogspot.com/2009/11/embed-code-syntax-highlighting-in-blog.html

สรุป FIS

จากบทความที่ผ่านมาเรื่อง Fuzzy Inference System
หากจะสรุปขั้นตอนโดยย่อ สามารถสรุปได้ดังภาพต่อไปนี้ครับ (ที่มา: fuzzy logic toolbox user's guide, www.mathworks.com) ซึ่งภาพนี้อธิบายหลักการทั้งหมดได้ดีและเข้าใจได้ง่ายด้วยครับ

Monday, March 29, 2010

Fuzzy Inference System

Fuzzy Inference System หรือ FIS คือระบบที่ทำการหาข้อสรุป ผลลัพธ์ โดยใช้กระบวนการทาง Fuzzy logic
เช่น จะต้องการตอบโจทย์ที่ว่า

"ควรจะให้ทิปพนักงานร้านอาหารเท่าใด ถ้าหากว่าปริมาณทิปขึ้นอยู่กับคุณภาพการบริการของพนักงาน และรสชาติของอาหาร"

โดยทั่วไป FIS จะมีสองแบบคือ แบบ Mamdini และ แบบ Sugeno ซึ่งหลักการใกล้เคียงกัน แต่ต่างกันที่ขั้นตอนการได้มาซึ่งผลลัพธ์ (Defuzzification)

ขั้นตอนการหาข้อสรุปโดยใช้ FIS จะเป็นดังนี้
1. กำหนด Input และ Output
Input:
1. คุณภาพการบริการ (Service) แบ่งเป็น 3 ระดับ แย่ (poor)  ดี (good) ดีมาก (excellent) อยู่ในช่วง 0-10
2. รสชาติของอาหาร (Food) แบ่งเป็น 2 ระดับ แย่ (poor)  ดีมาก (excellent) อยู่ในช่วง 0-10
Output:
ทิป แบ่งเป็น 3 ระดับ น้อย (small) ปานกลาง (normal) มาก (large) อยู่ในช่วง 0-30%

สมมติว่า ในโจทย์ข้อนี้ เราต้องการหาค่าทิป เมื่อ คุณภาพการบริการอยู่ที่ 3 และ รสชาติของอาหารอยู่ที่ 8

2. กำหนดดีกรีของ Input และ Output ให้สัมพันธ์กับระดับที่กำหนดไว้
ขั้นตอนนี้ก็คือการกำหนดรูปร่างของ membership function นั่นเอง ซึ่งมีหลายรูปแบบเช่น สามเหลี่ยม คางหมู gaussian ฯลฯ และมักจะถูกกำหนดโดยอาศัยประสบการณ์ หรือแบบสอบถามจากผู้เชี่ยวชาญต่างๆ

สมมติว่าเรากำหนดดังนี้
Input:
1. Service เป็นแบบ gaussian



2. Food เป็นแบบ trapezoid

Output:
Tip เป็นแบบ triangle


3. กำหนดกฎ (Rules) ของการตัดสินใจ
Rule 1: ถ้าบริการแย่ หรือ รสชาติแย่ จะทิปน้อย
Rule 2: ถ้าบริการดี จะทิปปานกลาง
Rule 3: ถ้าบริการดีมาก หรือ รสชาติดีมาก จะทิปมาก

4. ทำการ Fuzzy Input นั่นคือหาว่า ถ้า Input เป็นเท่านี้ จะได้ดีกรีเท่าใด โดยดูจากกราฟของ membership function ในข้อ 2 และกฎที่ตั้งไว้ในข้อ 3 ตัวอย่างเช่น
Rule 1: ถ้าบริการแย่ หรือ รสชาติแย่ จะทิปน้อย
จากโจทย์ ตอนนี้ Input คือ service = 3, food =  8

สนใจข้อความแรก ถ้าบริการแย่ จะได้ดีกรีเท่าใด
ดังนั้น เราจะเลือกกราฟของ bad service แล้วหาค่าดีกรี เมื่อ service = 3 (เส้นสีแดง) ซึ่งจะได้ ดีกรีประมาณ 0.2 (ค่าตามแกน y)
เช่นเดียวกัน สำหรับข้อความที่สอง ถ้ารสชาติแย่ จะได้ดีกรีเท่าใด
ดังนั้น เราจะเลือกกราฟของ bad food แล้วหาค่าดีกรี เมื่อ food = 8
จะได้ดีกรีเป็น 0 เพราะไม่ตัดกราฟเลย ตามรูป
จากกฎข้อแรกนี้ คือ "ถ้าบริการแย่ หรือ รสชาติแย่ จะทิปน้อย" สังเกตว่าเราเชื่อมต่อทั้งสองกรณีด้วยคำว่า หรือ ซึ่งก็คือ OR หรือค่า max ตามหลักการของ fuzzy logic นั่นเอง 

ดังนั้น จะได้ว่าดีกรีของกฎที่ 1 จะเท่ากับ max(0.2, 0) = 0.2

เมื่อพิจารณาทุกกฎตามหลักการนี้แล้ว ก็จะได้ผลลัพธ์ดังภาพต่อไปนี้



นั่นคือ
จากกฎข้อ 1 ได้ดีกรีประมาณ 0.2
จากกฎข้อ 2 ได้ดีกรีประมาณ 0.5
จากกฎข้อ 1 ได้ดีกรีประมาณ 0.5

5. ทำการ implication เพื่อหาผลลัพธ์ของแต่ละกฎ
เมื่อได้ดีกรีตามแต่ละกฎแล้ว ขั้นตอนต่อไปคือการเอาไปเปรียบเทียบกับ membership function ของผลลัพธ์ที่คาดการณ์ไว้ ซึ่งจะมีการใช้โอเปอร์เรเตอร์ได้หลายรูปแบบ ที่นิยมกันก็ืคือ ค่าต่ำสุด (min) นั่นคือ เมื่อเอาดีกรีไปเทียบกับกราฟผลลัพธ์ทางขวามือ ผลลัพธ์คือพื้นที่กราฟที่อยู่ใต้ค่าดีกรีนั้น ดังรูปต่อไปนี้ซึ่งแสดงผลลัพธ์ของกฎแรก

Rule 1: ถ้าบริการแย่ หรือ รสชาติแย่ จะทิปน้อย

ให้สังเกตว่า เราจะต้องเลือกกราฟให้สอดคล้องกับกฎด้วย เช่น กราฟผลลัพธ์ก็ต้องเลือกส่วนที่เป็น "ทิปน้อย" มาพิจารณา ไม่ได้เอามาทั้งหมด

เมื่อพิจารณาทุกกฎแล้ว จะได้ผลลัพธ์ดังนี้



6. รวมผลลัพธ์ทั้งหมดเข้าด้วยกัน (aggregation)
จากขั้นตอนที่ 5 เราจะได้ผลลัพธ์ของแต่ละกฎในรูปของ fuzzy set (ค่าประมาณในแต่ละกฎว่าผลลัพธ์จะอยู่ในช่วงใด) จากนั้นเราจะรวมผลลัพธ์ทั้งหมดเข้าด้วยกัน ซึ่งก็จะต้องมีวิธีการรวม ว่าจะใช้โอเปอร์เรเตอร์แบบใดอีก ซึ่งในที่นี้จะใช้การรวมแบบ max คือเอาค่าสูงสุดที่เป็นไปได้จากผลรวม

ผลลัพธ์จากการรวมแบบหาค่าสูงสุด


7. ทำการ Defuzzification เพื่อหาผลลัพธ์สุดท้าย
ผลลัพธ์ของขั้นตอนที่ 6 ยังอยู่ในรูปแบบของ fuzzy set นั่นคือ คำตอบที่ได้ควรจะอยู่ในพื้นที่ของกราฟที่กำหนด เืพื่อที่จะให้ได้ผลลัพธ์ที่ออกมาชัดเจนเป็นตัวเลข เช่น ทิปจะเป็นกี่เปอร์เซนต์ จำเป็นต้องแปลงค่า fuzzy set ให้เป็นเลขตัวเดียว ขั้นตอนนี้เรียกว่า Defuzzification ซึ่งทำได้หลายแบบ เช่น การหาค่า centroid ของพื้นที่, การหาค่าสูงสุดของทั้งหมด, การหาค่าต่ำสุดของค่าสูงสุดทั้งหมด ฯลฯ ในที่นี้จะใช้การหาค่า centroid ซึ่งจะได้ผลลัพธ์ดังเส้นสีแดงในรูปต่อไปนี้ ซึ่งจะได้ว่าค่าทิปควรจะเป็น 18% โดยประมาณ



โดยสรุป ขั้นตอนทั้งหมดของ FIS จะสามารถแสดงได้ดังรูป

 

เสร็จเรียบร้อยแล้วครับ การแก้ปัญหาโดยใช้ Fuzzy Inference System

Saturday, March 27, 2010

ติดตั้ง OpenCV 2.0 กับ Dev-C++

ขั้นตอน


1. ดาวน์โหลด Dev-C++ จาก http://www.bloodshed.net/dev/devcpp.html และติดตั้งให้เรียบร้อย 


2. ดาวน์โหลด OpenCV 2.0 จาก http://sourceforge.net/projects/opencvlibrary/files/opencv-win/2.0/ 


3. ติดตั้ง OpenCV โดยสมมติว่าติดตั้งที่ c:\OpenCV2.0

4. เรียกใช้งาน Dev-C++ เลือกเมนู Tools/ Compiler Options
สร้างโปรไฟล์ของคอมไพเลอร์ใหม่ให้ชื่อว่า OpenCV (ขั้นตอนนี้อาจจะไม่ต้องทำก็ได้) จากนั้นกำหนดพารามิเตอร์ของ linker ดังนี้
-llibcxcore200 -llibcv200 -llibcvaux200 -llibhighgui200 -llibml200
ดังรูปต่อไปนี้ 


5. คลิกเลือกแทบ Directories เพิ่มรายละเอียดในหัวข้อ Binaries ดังนี้ 


6. เพิ่มรายละเอียดในหัวข้อง Libraries และ C++ Includes ดังนี้
7. เข้าไปที่ include ไดเรกทอรีของ OpenCV 2.0 เช่น c:\OpenCV2.0\include\opencv แล้วแก้ไขไฟล์ชื่อ cxoperations.hpp แถวๆบรรทัดที่ 68 จากเดิม

#if __GNUC__ >= 4
เปลี่ยนเป็น
#if __GNUC__ >= 4 || __MINGW32__
บันทึกไฟล์นี้
8. ทดสอบโปรแกรมกันง่ายๆ ตามนี้ครับ สมมติว่ามีรูปชื่อ lena.jpg ในไดเรกทอรีเดียวกับตัวโค้ดนะครับ

9. จากนั้นก็คอมไพล์ แล้วรันดูผลลัพธ์ครับ

ขอบคุณข้อมูลต้นฉบับจาก
1. http://n2.nabble.com/Configuration-help-OpenCV-2-0-with-Dev-Cpp-td3943011.html
2. http://opencv.willowgarage.com/wiki/InstallGuide