2009年12月17日 星期四

JNI

使用 JNI 最保險的方式是透過 LoadLibrary 導入 jvm.dll。
首先設好 include 和 lib path,這個網路上資料很多,可自行查找。

錯誤排解:

[1] 發生 Java 異常 EXCEPTION_ACCESS_VIOLATION (0xc0000005):
表示 jvm.dll 位置有誤。
直接指定到 /jre/bin/client/ 去 LoadLibrary 就行,
任何「多餘」的拷貝、指定動作都可能引起此異常。

[2] JNI_CreateJavaVM 傳回 JNI_ERR:
「可能」是先前安裝過多個 JDK 或 JRE 版本,
因未移除,或未移除乾淨,互相干擾所致。
解法是直接 load jvm.dll。

[3] Dev C++ 如何加 -ljvm?
沒時間試驗,但至少 lib path 要設對。

Test.java
public class Test 
{
    public static void hello () {
         System.out.print ("hello~");
    }    
    public static void main (String[]s) {}
}
以 javap -s -p Test 觀察各 method 的 Signature,
供 GetStaticMethodID 識別調用。
#undef UNICODE
#pragma comment (lib,"jvm.lib")
#include "windows.h"
#include "jni.h"
#include <iostream>
using namespace std;

#define CHECK(o) if(o); else{\
    cout<<__FILE__<<" ("<<__LINE__<<"): not satisfy "<<#o ;\
    exit (getchar());\
    }

typedef jint (WINAPI* PFunCreateJavaVM) (JavaVM**, void**, void*);

int main ()
{
    JavaVMOption   opt[4]  = {
        {"-Djava.compiler=NONE",0},
        {"-Djava.class.path=." ,0},
    };
    JavaVMInitArgs vm_args = {
        JNI_VERSION_1_2, 2, opt, JNI_TRUE      //opt = JRE 運行參數
    };  
    JNIEnv* env = 0;
    JavaVM* jvm = 0;
   

    HINSTANCE hInst = LoadLibrary ("C:/Java/jre/bin/client/jvm.dll");
    CHECK (hInst != 0);

    PFunCreateJavaVM CreateJavaVM = 
        (PFunCreateJavaVM) GetProcAddress (hInst, "JNI_CreateJavaVM");
    
    CreateJavaVM (&jvm, (void**)&env, &vm_args);
    CHECK (env != 0);

    jclass  cls = env->FindClass ("Test");      //查找 Java 類別
    CHECK (cls != 0);

    jmethodID pfn = env->GetStaticMethodID (cls, "hello", "()V");
    CHECK (pfn != 0);
    env->CallStaticVoidMethod (cls, pfn);

    if (env->ExceptionOccurred ()) {
        env->ExceptionDescribe ();
    }
    jvm->DetachCurrentThread ();
    jvm->DestroyJavaVM ();

    return getchar();
}

2009年12月12日 星期六

堆疊式線段追蹤

 
用 recursion 在線段複雜時會造成 stack overflow,
因此需要 heap 解。

LineTrace 提供了 singleton 介面,但不建議在大型專案中使用,
因為它除了有 global variable 的缺點外,
和其他類別合作時,會引入建構解構上的相依性,難以 debug。
應盡量把 LineTrace 設計成 class component 來用。

#include "global.h"
#include "help_fn.h"
#include "new_arr.h"

//-------------------------------------------------------------
// state 紀錄圖中每一點的追蹤狀態:
//
//      678
//      5米1
//      432
//
// 狀態 = 0 表示非邊線點
//-------------------------------------------------------------

class LineTrace
{
    enum {W=512, H=512};         //狀態圖的最大寬高

    int        w, h;             //檢測邊界
    IplImage*  out;              //輸入邊線圖, 同時用來存放輸出結果
    IplImage*  mark;             //阻隔點, 遇阻隔點則不再追蹤下去

    int        nPos;             //堆疊頂端指標
    int*       posx;             //紀錄追蹤位置的堆疊
    int*       posy;             //
    uchar*     state;            //標示圖中每一點的追蹤狀態

    static LineTrace* self;      //Singleton

    void check_vaild()           //確認資料的有效性
    {
        if (out == 0) ERROR_MSG_("out == 0");
        if (mark== 0) ERROR_MSG_("mark == 0");
        if (out->width  > W) 
            ERROR_MSG3_("影像寬度: %d > %d", out->width, W);
        if (out->height > H) 
            ERROR_MSG3_("影像高度: %d > %d", out->height, H);
    }

public:

    static LineTrace& instance()
    {
        if (self == 0) self = new LineTrace;
        return *self;
    }
    static void release()
    {
        delete self;
    }

    LineTrace()
    {
        posx  = new int  [W*H];
        posy  = new int  [W*H];
        state = new uchar[W*H];
    }
   ~LineTrace()
    {
        delete[] posx;
        delete[] posy;
        delete[] state;
    }
    void setImage (IplImage* out_, IplImage* mark_)
    {
        out  = out_;
        mark = mark_;
        check_vaild();
    }

    void trace (int ox, int oy)         // (ox,oy) = trace 的起始座標
    {
        static CvScalar red = cvScalar (0,255,0);
        int x, y;                           //目前 trace 到的點座標
        int dir;                            //目前要 trace 的方位
        CvScalar c, c2;

        check_vaild();
        nPos = 0;

        #define PUSH(px,py) \
            nPos++;\
            posx[nPos] = x = px;\
            posy[nPos] = y = py;\
            dir = state[nPos] = 1;

        #define POP \
            nPos--;\
            x = posx[nPos];\
            y = posy[nPos];\
            dir = state[nPos];

        PUSH (ox,oy);                       //邊界衛兵 :-)
        PUSH (ox,oy);                       //將起始座標推入堆疊
                                            //開始進行線段追蹤
        while (nPos > 1) 
        {
            if (nPos >= W*H) 
                ERROR_MSG_("stack overflow!");

            if (dir == 1) {                 //若是第一次檢測某個座標點
                c  = cvGet2D (out,  y, x);  //就取出顏色值
                c2 = cvGet2D (mark, y, x);

                if (c.val[0] == 0 ||        //若不是邊緣線
                    c2.val[0] > 0){         //或是遇到阻礙點 
                    POP;                    //便回復先前的狀態
                    continue;               //退回上個節點
                }
                cvSet2D (out, y, x, red);   //標示追蹤過的線
            }
            if (dir <= 0) {                 //這是不可能發生的
                puts ("error!"); getch();
            }
            if (dir > 8) {                  //若所有方向都檢測過了
                POP                         //便回復先前的狀態     
                continue;                   //退回上個節點
            }
            else {
                state[nPos]++;              //遞增目前節點的方向值
                switch (dir) {              //移至特定方位的鄰居點       
                    case 1: x++;      break;
                    case 2: x++; y++; break;
                    case 3:      y++; break;
                    case 4: x--; y++; break;
                    case 5: x--;    ; break;
                    case 6: x--; y--; break;
                    case 7:      y--; break;
                    case 8: x++; y--; break;    
                }
                PUSH (x,y);                 //將鄰居點推入堆疊頂端
            }                               //進行邊線追蹤
        }
        #undef PUSH
        #undef POP
    }
};

LineTrace* LineTrace::self = 0;


#define ef else if 


//-------------------------------------------------------------
// 說明:
//     壓著左鍵移動滑鼠  - 畫出線條
//     按中間的滾輪鍵    - 加入阻隔點
//     在線段上按右鍵    - 進行線段追蹤
//     按空白鍵         - 重置畫面
//     按 ESC          - 離開程式 
//-------------------------------------------------------------

struct Painter
{
    static LineTrace tracer;                //線段追蹤器
    static IplImage* src;                   //畫板
    static IplImage* mark;                  //阻隔點
    static CvPoint   pt;                    //先前的點座標

    Painter (char* path = 0)                //可傳入黑白圖檔
    {
        if (path) src = cvLoadImage (path, CV_LOAD_IMAGE_GRAYSCALE);
        else      src = cvCreateImage (cvSize (400,400), 8, 3);

        mark = cvCreateImage (cvGetSize (src), 8, 3);
        reset();
        tracer.setImage (src, mark);
        cvNamedWindow ("image", 1);
        cvSetMouseCallback ("image", on_mouse, 0);
    }
   ~Painter()
    {                                       //釋放資源
        cvDestroyWindow ("image");
        cvReleaseImage (&src);
        cvReleaseImage (&mark);
    }
    static void reset()                     //清除畫面
    {
        cvZero (src);
        cvZero (mark);
    }

    static void on_mouse (int event, int x, int y, int flags, void*)
    {
        static CvScalar white = CV_RGB (255,255,255);

        if (src == 0) return;
     
        if (event == CV_EVENT_LBUTTONUP)        //如果放開了滑鼠左鍵
            //!(flags & CV_EVENT_FLAG_LBUTTON))   //或按下了其他鍵
            pt = cvPoint (-1,-1);               //便重置描繪起始點

        ef (event == CV_EVENT_MBUTTONUP) 
        {
            cvCircle (mark, cvPoint(x,y), 4, white, -1);
            cvCircle (src,  cvPoint(x,y), 4, CV_RGB(0,180,255), -1);
            show();
        }
        ef (event == CV_EVENT_RBUTTONUP) 
        {
            puts ("start tracing");
            for (int j,i=-1; i<2; ++i)
                for (j=-1; j<2; ++j)
                    tracer.trace (x+i, y+j);
            puts ("end tracing");
            show();
        }        
        ef (event == CV_EVENT_LBUTTONDOWN)
            pt = cvPoint (x,y);
        ef (event == CV_EVENT_MOUSEMOVE &&      //若移動滑鼠
            (flags & CV_EVENT_FLAG_LBUTTON) )   //且壓著左鍵
        {
            CvPoint p = cvPoint (x,y);
            if (pt.x < 0) pt = p;
            cvLine (src, pt, p, white, 1, 8, 0);
            pt = p;
            show();
        }
    }
    static void show()
    {
        cvShowImage ("image", src);
    }
};

LineTrace Painter::tracer;
IplImage* Painter::src;
IplImage* Painter::mark;
CvPoint   Painter::pt;


int main()
{
    Painter painter;
    painter.show();
    
    while (true)
    {
        int key = cvWaitKey(0);
 
        if (key == 27)  break;
        ef (key == ' ') {
            painter.reset();
            painter.show();
        }
    }
}

執行結果:
畫了張圖做測試~~~

2009年12月10日 星期四

老鼠走迷宮

 
步驟:
[1] 往上下左右拜訪鄰接點。
[2] 若鄰點可通行,便遞迴至 [1]。

內有暴力 rand() ...
#include <windows.h>
#include <ctime>
#include <iostream>
using namespace std;

const int W = 23, H = 32;           //迷宮長寬
wchar_t   m[W+1][H+1] = {};         //迷宮本體
int  X, dX[] = {0,-2,0,2}, d;       //方位角
int  Y, dY[] = {2,0,-2,0};
bool bEnd = false;                  //是否已走到終點

void make (int x, int y) 
{
    m[x][y] = L' ';
    for (int n = 16; n--; ) { 
        d = rand() % 4;
        X = x + dX[d];
        Y = y + dY[d];
        if (0<X && X<W && 0<Y && Y<H && 
            m[X][Y] == L'■')
            m[x+dX[d]/2][y+dY[d]/2] = L' ',
            make (X,Y);
   }
}
void show_step (int x, int y, wchar_t ch)  
{
    COORD c = {y*2, x};             //顯示過程
    SetConsoleCursorPosition (
        GetStdHandle (STD_OUTPUT_HANDLE), c);  
    wcout << (m[x][y] = ch);
    Sleep (50);        
}
void visit (int x, int y)               //尋路
{
    if (x==X && y==Y) bEnd = true;      //已抵達終點
    else if (m[x][y] == L' ' && !bEnd) {
        show_step (x, y, L'˙');
        for (int i=0; i<4; ++i) 
            visit (x+dX[i]/2, y+dY[i]/2);
        if (!bEnd) show_step (x, y, L' ');        
    }
}
int main (/* daviddr 091211 */)
{
    srand (time(0));
    wcout.imbue (locale ("cht"));
    for (d=W; d--;) wmemset (m[d], L'■', H)[H] = L'\n';
    make (1,1);

    X = W-2;                        //記錄終點座標
    Y = H-1;
    m[1][0] = m[X][Y] = L'◎';
    wcout << *m;
    visit (1,1);
    getchar();
}
執行結果:
■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ ◎˙˙˙˙˙■˙˙˙˙˙■      ˙˙˙˙˙˙˙■         ■ ■■■■■˙■˙■■■˙■ ■■■■■˙■■■■■˙■ ■■■■■■■ ■ ■˙˙˙˙˙■˙˙˙■˙■     ■˙■   ■˙■ ■ ■˙˙˙˙˙■ ■˙■■■■■ ■˙■˙■■■■■■■˙■■■ ■˙■ ■ ■˙■■■˙■ ■˙˙˙■   ■˙■˙■˙˙˙˙˙■˙■   ■˙■   ■˙■ ■˙■ ■■■˙■ ■■■˙■˙■˙■■■˙■˙■ ■■■˙■■■ ■˙■ ■˙■ ■˙˙˙■   ■˙■˙˙˙■ ■˙■˙■ ■˙˙˙■   ■˙■ ■˙■ ■˙■■■■■■■˙■■■■■ ■˙■˙■ ■˙■■■■■■■˙■ ■˙■ ■˙˙˙˙˙˙˙˙˙■     ■˙˙˙■˙˙˙■  ˙˙˙˙˙■˙˙˙■ ■■■■■■■■■■■■■ ■ ■■■■■˙■■■■■˙■■■■■˙■■■ ■       ■     ■     ■˙˙˙˙˙˙˙■˙˙˙˙˙■ ■ ■ ■■■■■ ■ ■■■■■■■■■ ■■■■■■■■■˙■■■■■ ■ ■     ■   ■˙˙˙■       ■˙˙˙  ■˙■     ■ ■ ■■■ ■■■ ■˙■˙■■■■■■■ ■˙■˙■ ■˙■■■■■ ■ ■ ■ ■   ■ ■˙■˙˙˙˙˙˙˙■ ■˙■˙■ ■˙˙˙˙˙■ ■ ■ ■ ■■■ ■■■˙■■■■■■■˙■■■˙■˙■■■■■■■˙■ ■ ■   ■ ■   ■˙˙˙˙˙■ ■˙˙˙˙˙■˙˙˙˙˙˙˙˙˙■ ■ ■■■ ■ ■■■ ■■■■■˙■ ■■■■■■■■■■■■■■■■■ ■ ■ ■ ■   ■   ■˙˙˙■    ˙˙˙■  ˙˙˙■˙˙˙˙˙■ ■ ■ ■ ■ ■■■ ■˙■■■■■■■˙■˙■■■˙■˙■˙■■■˙■ ■     ■   ■  ˙˙˙˙˙˙˙˙˙■˙˙˙˙˙■˙˙˙■  ˙◎ ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■

使用 shuffle 可以隨機拜訪鄰接點,
而 make 中的 for 只要施行 4 次 (轉折點也會變多...(吧?))。

隨機拜訪鄰點,或許能稍微縮短抵達終點的平均時間
(但也可能更久,需視迷宮結構而定)。
#include <algorithm>

void visit (int x, int y)           //尋路
{
    if (x==X && y==Y) {             //已抵達終點
        bEnd = true;
        return;
    }
    if (m[x][y] == L' ' && !bEnd) {
        show_step (x, y, L'˙');
        int i, D[] = {0,1,2,3};     //鄰點序列
        random_shuffle (D, D+3);    //隨機拜訪鄰點  
        for (i=0; i<4; ++i)            
            visit (x+dX[D[i]]/2, y+dY[D[i]]/2);
        if (!bEnd) 
            show_step (x, y, L' ');        
    }
}


OEM懷舊顯示 :-)
#include <windows.h>

void setCodePage (int n...)
{
    va_list p;
    va_start (p, n);
    while (!SetConsoleOutputCP(n)) 
        n = va_arg (p, int);
}

void main ()
{
    int W=20, i;
    char *p, const*const a =     
        "XXXXXXXXXXXXXXXXXXXX"
        "X     X            X"
        "X XXX X X XXX XX X X"
        "X X X X X   X      X"
        "X       X X X X  X X"
        "X X X X XXX X      X"
        "X     X X X   X  X X"
        "X X X   X XXX X  X X"
        "X     X            X"
        "X XXXXXXXXXXXXXXXXXX";      
   
    setCodePage (437, 855, 10000);

    for (p=a, i=0; *p; i++) {
        if (i>=W) { puts(""); i=0; }        
        printf ("%c", *p++=='X'?219:' ');
    }
}

2009年12月9日 星期三

無框視窗

 
使用 Dev C++ 的話,連結器欄需加入:-lgdi32
#include <windows.h>
#include <iostream>
using namespace std;
extern "C" WINBASEAPI HWND WINAPI GetConsoleWindow();

void set_size (int w, int h)     
{
    int cy = GetSystemMetrics (SM_CYCAPTION),
        bx = GetSystemMetrics (SM_CXBORDER),
        by = GetSystemMetrics (SM_CYBORDER),
        fx = GetSystemMetrics (SM_CXFIXEDFRAME),
        fy = GetSystemMetrics (SM_CYFIXEDFRAME);

    if (w > 640) w = 640;
    
    HANDLE     hOut = GetStdHandle (STD_OUTPUT_HANDLE);
    HWND       hwnd = GetConsoleWindow ();
    SMALL_RECT rc   = {0, 0, w/8 ,h/16};
    COORD      size = {w/8+1 ,h/16+1};    
    
    int dx = fx+bx;
    int dy = cy+fy+by;

    SetConsoleWindowInfo (hOut,1,&rc);          //變更視窗大小
    SetConsoleScreenBufferSize (hOut, size);
    SetWindowRgn (hwnd, CreateRectRgn (dx, dy, w-dx, h-dy), 1);
}

int main (/* 091210 */)   
{
    set_size (490, 690);
   
    cout << "frameless window";
    return getchar(); 
}

Simple K-Means

 
參考了 Adrien Le Batteux 和 OpenCV 的 kmeans2 範例。
不過寫的落落長,實在不怎麼 simple @@

點集分群結果:


程式碼:
struct FontObject 
{
    char     str[256];
    CvFont   font;                              //字型物件
    CvPoint  pos;
    CvScalar color;

    FontObject (double sw=0.25, double sh=0.25, int thickness = 1)
    {
        color = cvScalar (255,255,255);
        cvInitFont (&font, CV_FONT_HERSHEY_SIMPLEX,
            sw,sh, 0, thickness, CV_AA);
    }
    void show (IplImage* out, int x, int y)
    {
        pos.x = x;
        pos.y = y;
        sprintf (str, "(%d,%d)", x, y);
        cvPutText (out, str, pos, &font, color);
    }
    void show (IplImage* out, int x, int y, char* txt)
    {
        pos.x = x;
        pos.y = y;
        cvPutText (out, txt, pos, &font, color);
    }
};

FontObject fontObj (0.75, 0.75, 2);


template <typename T>
void shuffle (T* a, int n)        //簡易洗牌
{
    while (n > 1) {
        int j = rand() % n; n--;
        T t = a[j]; a[j] = a[n]; a[n] = t;
    }
}

template <typename T>
inline void set_vector (T* out, T* in, int n)
{
    while (n-- > 0) out[n] = in[n]; 
}

template <typename T>
inline void add_vector (T* out, T* in, int n)
{
    while (n-- > 0) out[n] += in[n]; 
}

template <typename T>
inline void div_vector (T* v, int divisor, int n)
{
    while (n-- > 0) v[n] /= divisor; 
}

template <typename T>
inline double cal_distance (T* a, T* b, int n)
{
    double sum = 0, diff;
    while (n-- > 0) {
        diff = a[n] - b[n];
        sum += diff * diff;
    }
    return sqrt (sum);
}

template <typename T = float>
struct Kmeans
{
    int    nIteration;              //最大迭代次數
    int    nData;                   //資料項數
    int*   label;                   //資料項的分類標籤
    int*   ccount;                  //群心涵蓋的資料個數
    T**    data;                    //指向資料集合
    T**    center;                  //群心集合 
    T*     new_center;              //計算暫用的群心 
    T*     mean;                    //資料項的幾何中心 
    int    nCenter;                 //群心個數 
    int    dim;                     //資料維度
    double error;                   //群心更新時的最大移動量
    
    Kmeans() {
        srand (time(0)); 
        label  = ccount = 0;
        center = data   = 0;
        new_center = mean = 0; 
    }
   ~Kmeans() {
        release();
    }

private:

    void set (T* out, T* in)      {set_vector (out, in, dim);}
    void add (T* out, T* in)      {add_vector (out, in, dim);}
    void div (T* v, int divisor)  {div_vector (v, divisor, dim);}
    double distance (T* a, T* b)  {return cal_distance (a, b, dim);}

    void release()                          //釋放資源
    {
        if (label)      del_arr (label);      label  = 0;
        if (ccount)     del_arr (ccount);     ccount = 0;
        if (center)     del_arr (center);     center = 0;
        if (mean)       del_arr (mean);       mean   = 0;
        if (new_center) del_arr (new_center); new_center = 0;
    }

    void init_centers ()
    {
        if (nCenter < 1)     ERROR_MSG_("群心數目需 > 1");
        if (nCenter > nData) nCenter = nData; 

        release();                          //釋放資源
        
        new_center = (T*) new_arr (sizeof(T), 1, dim);
        mean   = (T*)   new_arr (sizeof(T),   1, dim);
        center = (T**)  new_arr (sizeof(T),   2, nCenter, dim);
        ccount = (int*) new_arr (sizeof(int), 1, nData);
        label  = (int*) new_arr (sizeof(int), 1, nData);

        int i;

        for (i=0; i<nCenter; ++i)           //隨機指定樣本點為群心
            set (center[i], data [rand() % nData]);
        
        for (i=0; i<nData; ++i)             //計算資料項的幾何中心
            add (mean, data[i]);
        div (mean, nData);
        
    }

    void cluster_assignment ()              //將樣本分群
    {
        int     label_id, j, k;
        double  min_dist, dist;
        
        memset (ccount, 0, nData* sizeof(int));

        for (j=0; j<nData; ++j) {
            label_id = 0;
            min_dist = distance (center[0], data[j]);
            for (k=1; k<nCenter; ++k) {
                dist = distance (center[k], data[j]);
                if (dist < min_dist) {
                    label_id = k;
                    min_dist = dist;
                }
            }
            label[j] = label_id;            //歸類
            ccount[label_id]++;             //統計群心涵蓋的資料個數 
        }
    }

    void update_cluster ()                  //重新估算 centroids 位置
    {
        int j, k, total;
        double dist = 0;
        error = 0;

        for (k=0; k<nCenter; ++k)           
        {
            memset (new_center, 0, dim* sizeof(T));
            total = 0;
            for (j=0; j<nData; ++j) 
                if (label[j] == k) {
                    add (new_center, data[j]);
                    total++;
                }      
            if (total > 0) {           
                div (new_center, total); 
                dist = distance (center[k], new_center);
                if (error < dist) 
                    error = dist;
                set (center[k], new_center);
            }
            else            
                set (center[k], mean);            
        }        
    }

public:

    //算法主流程: nCenter = 群心數目, 
    //            data    = 資料集合 = data [nData_] [dim_] 

    void do_cluster 
        (int nCenter_, double error_threshold, 
         T** data_, int nData_, int dim_)
    {
        nCenter = nCenter_;                 //設定群心數目
        data    = data_;                    //指向資料集合
        nData   = nData_;                   //設定資料個數
        dim     = dim_;                     //設定資料維度
        
        init_centers ();                    //初始化群心位置
             
        for (int i=0; i<nIteration && error > error_threshold; ++i) {
            cluster_assignment ();          //將樣本分群            
            update_cluster ();              //重新估算 centroids 位置
        }
    }


    //單步執行 clustering, 回傳群心最大移動量

    void setup (int nCenter_, T** data_, int nData_, int dim_)
    {
        nCenter = nCenter_;                 //設定群心數目
        data    = data_;                    //指向資料集合
        nData   = nData_;                   //設定資料個數
        dim     = dim_;                     //設定資料維度        
        init_centers ();                    //初始化群心位置
    }
    double iterater_cluster ()
    {
        cluster_assignment ();              //將樣本分群            
        update_cluster ();                  //重新估算 centroids 位置
        return error;
    }
};



//-----------------------------------------------------------------

template <class T>
struct KMeanTest
{
    enum {CD = 3, 
          MAX_CLUSTERS = (CD+1)*(CD+1)*(CD+1),  //最大群心數
          W=700, H=700};                        //展示影像的寬高
    bool bEnd;

    CvScalar color_tbl[MAX_CLUSTERS];
    IplImage* img;                          //2D展示影像
    Kmeans<T> kmean;
    T** data;                               //資料集合
    int nCenter;                            //群心個數
    int nData;                              //資料項數
    int dim;                                //資料維度

private:
    void release()                          //釋放資源
    {
        if (data) del_arr (data); data = 0;
    }
    void generate_color_table()
    {
        int r, g, b, i=0, d = 255/CD;
        for (r=0; r<256; r+=d)
            for (g=0; g<256; g+=d)
                for (b=0; b<256; b+=d)
                    color_tbl[i++] = CV_RGB (r,g,b);
        shuffle (color_tbl+1, MAX_CLUSTERS-2);
    }
    void generate_sample ()
    {      
        release();
        data = (T**) new_arr (sizeof(T), 2, nData, dim);
        for (int i=0; i<nData; ++i) {
            data[i][0] = rand() % W;
            data[i][1] = rand() % H;
        }
    }

public:
    KMeanTest()
    {
        generate_color_table();
        cvNamedWindow ("clusters", 1);
        data = 0;
        img = cvCreateImage (cvSize (W, H), 8, 3);
    }
   ~KMeanTest()
    {
        cvDestroyWindow ("clusters");
        cvReleaseImage (&img);
        release();
    }
    void show()
    {
        static CvScalar white = CV_RGB (255,255,255);
        static char txt[9];
        int i, key;
        
        cvZero (img);
        for (i=0; i<nData; ++i)                 //畫出資料項
        {
            CvPoint  pt    = cvPoint (data[i][0], data[i][1]);            
            CvScalar color = color_tbl [kmean.label[i] + 1];
            cvCircle (img, pt, 2, color, CV_FILLED, CV_AA, 0);
        }
        for (i=0; i<nCenter; ++i)              //畫出群心位置
        {            
            T* c = kmean.center[i];
            cvCircle (img, cvPoint (c[0],c[1]), 3, 
                white, CV_FILLED, CV_AA, 0);
            sprintf (txt, "%d", i);            //顯示群心標號
            fontObj.show (img, c[0]+3, c[1], txt); 
        }
        cvShowImage ("clusters", img );
        key = cvWaitKey(0);
        if (key == 27 || key == 'q' || key == 'Q')  //ESC
            bEnd = true;
    }

    void start (int nCenter_, int nData_, int dim_, double threshold)
    {
        bEnd    = false;
        nCenter = nCenter_;
        nData   = nData_;
        dim     = dim_;

        if (nCenter > MAX_CLUSTERS-2) {
            cout << "群心數目不得大於 " << MAX_CLUSTERS-2;
            return;
        }

        generate_sample ();
        kmean.setup (nCenter, data, nData, dim);

        while (!bEnd && 
               kmean.iterater_cluster() > threshold)
            show();
    }
};


int main ()
{
    KMeanTest<double> test;
    test.start(24, 10000, 2, 5);
}

2009年12月6日 星期日

simple iostream

 
改自:http://okmij.org/ftp/packages/iostream.h
#include <stdio.h>

static class Iostream {} cin, cout;
static class Endl{} endl;

Iostream& operator << (Iostream& o, Endl& data) {
    puts (""); return o;
}

Iostream& operator << (Iostream& o, void* data) {
    printf ("%x", data);
    return o;
}

Iostream& operator << (Iostream& o, bool data) {
    printf ("%s", data? "true": "false");
    return o;
}

Iostream& operator >> (Iostream& i, char* data) {
    scanf ("%s", data);
    return i;
}

#define DEF_OS(T,fmt) \
    Iostream& operator << (Iostream& o, T data) {\
        printf ("%"#fmt,data);\
        return o;\
    }\
    Iostream& operator >> (Iostream& i, T& data) {\
        scanf ("%"#fmt,&data);\
        return i;\
    }

DEF_OS (const char*  ,  s)
DEF_OS (char*        ,  s)
DEF_OS (char         ,  c)
DEF_OS (int          ,  d)
DEF_OS (float        ,  g)
DEF_OS (double       , lg)
DEF_OS (long         , ld)
DEF_OS (long long    , ll)
DEF_OS (unsigned     ,  u)
DEF_OS (unsigned long, lu)


int main ()
{
    char   s[64];
    double d;

    cin >> s >> d;
    cout << "input: " <<s <<' ' << d <<endl <<&d;
}

2009年11月30日 星期一

水仙花數

 
Narcissus Number 指的是「各個位數的立方和等於原數」的數。
列出 100~1000 間的那斯索斯~
應該還有更聰明的羅列方法..
int main ()
{
    #define C n[x] = x*x*x; x++
    int a=1, b=0, c=0, x=0; 
    int n[10]; {C,C,C,C,C,C,C,C,C,C;} 
     
    for (x=99; ++x < 1000; 
        ++c > 9 && (c=0, ++b > 9) && (b=0, ++a)) 
        if (x == n[a]+n[b]+n[c]) cout <<' '<< x;
}