Chủ Nhật, 21 tháng 9, 2014

Commit project C++ Visual Studio lên SVN thì nên commit loại file nào?

Giới thiệu


Nếu các bạn làm việc với Visual Studio C++ thì chắc cũng rõ là trong thư mục project có khá nhiều file lạ, không phải file source code.
Vấn đề là khi bạn muốn project cho người khác hoặc có ý định commit lên SVN thì nên chọn file nào, xóa file nào.
Thiếu file thì người khác down về không build được, dư file thì lãng phí. Đặc biệt là khi commit lên SVN thì lại nhập nhằng khi mấy cái file không liên quan bị thay đổi nội dung.

Chi tiết


Clean Project trước khi commit :)

Các lại file sau đây cần được commit:


ico: là file icon, thuộc loại file resoure của project
rc: resource script
rc2: resource script
sln: project file, là file quản lý thông tin của Solution
txt: project element, kiểu như file Readme. Nếu biết rõ nội dung của các file này thì chắc bạn cũng có thể quyết định được là có nên commit hay không.
vcxproj: project file, là file quản lý thông tin của project

Các file sau đây không cần thiết phải commit:


aps: last resource editor state, file lưu trữ trạng thái gần nhất của resource, giúp VS build nhanh hơn
exe: build result
idb: build state
ipch: build helper
lastbuildstate: build helper
lib: build result. Nếu file lib này là kết quả output của project bạn đang làm thì không cần thiết, nhưng nếu file này là thư viện ngoài và bạn cần nó để build project của bạn thì tôi nghĩ là nên commit.
log: build log
manifest: build helper
obj: build helper
pch: build helper
pdb: build result
res: build helper
sdf: intellisense dbase
suo: solution user options
tlog: build log

Tham khảo


stackoverflow.com 0ByOwrP8BqUhiMnRlMXZSODVTWW8

Thứ Bảy, 13 tháng 9, 2014

Hiển thị ảnh OpenCV (IplImage) trên MFC dialog


Giới thiệu


Bạn là một người sử dụng thư viện OpenCV cho công việc nghiên cứu Thị giác máy tính (Computer Vison) và thuật toán xử lý ảnh. Bạn muốn viết một chương trình demo kết quả nghiên cứu một cách trực quan và tương tác hơn. Windows Form là một giải pháp mà tôi cảm thấy thú ví nhất.
Trong bài viết này tôi sẽ giới thiệu đến các bạn cách hiển thị ảnh IplImage (thư viện OpenCV) lên MFC dialog.
Bài viết này tôi dành cho các bạn mới bắt đầu nghiên cứu OpenCV, và các bạn cần một ứng dụng Windows Form để demo thuật toán xử lý. Do đó tôi cố gắng trình bày ở mức dễ hiểu nhất có thể, hy vọng các bạn nắm bắt được phương pháp và mở rộng tính năng của ứng dụng để phục vụ tốt hơn cho công việc nghiên cứu.

Kiến thức nền


Ngôn ngữ lập trình: C/C++
Biết lập trình Windows Form MFC căn bản (trong bài viết này tôi sử dụng Visual Studio 10)
Biết sử dụng thư viện OpenCV (trong bài viết này tôi sử dụng phiên bản 3.0.0-alpha, các phiên bản khác cũng tương tự) trong Visual Studio.
  • Đã biết cách cấu hình thư viện OpenCV trong Visual Studio (có trình bày sơ lược trong bài viết này)
  • Đã biết cách tạo ứng dụng Win32 Console để sử dụng OpenCV

Chi tiết


Chúng ta sẽ tạo một ứng dụng dạng Dialog có thiết kế như hình bên dưới. Click vào nút Browse để chọn ảnh cần hiển thị. Sau khi chọn xong thì ảnh sẽ được hiển thị lên control của Dialog.

Nếu muốn thay đổi ảnh thì chỉ việc click và Browse phát nữa. Nếu muốn hiển thỉ cả ảnh gốc và kết quả của thuật toán thì chỉ việc thêm một control để hiển thị ảnh nữa là xong.
Về tổng quát thì giải pháp sẽ như sau:
  1. Tạo một MFC dialog,và thêm Picture Control (hoặc Static Text) vào dialog. Tạo buffer ảnh cho control này
  2. Load ảnh bằng hàm cvLoadImage() của thư viện OpenCV 
  3. Copy ảnh đã load vào buffer ảnh của control (có thể cần phải rescale lại ảnh để hiển thị lên control)
  4. Convert dữ liệu từ buffer ảnh sang đối tượng ảnh của MFC và vẽ lên control
Để thực hiện được các bước trên tôi xin trình bày chi tiết hơn ở các bước bên dưới:

Bước 1: Tạo MFC project trong Visual Studio.


Chọn File>New>Project
Chọn Visual C++>MFC>MFC Application. Đặt tên cho project là mfcopencv và click OK.
Thiết lập cho Application Type như sau:

Click Finish.

Bước 2: Chuẩn bị thư viện OpenCV


Trong thư mục chứa Project, tạo 2 thư mục inclib
Các bạn download và giải nén thư viện OpenCV.
Sau đó copy 2 thư mục opencvopencv2 vào thư mục inc đã tạo.
Copy file opencv_world300.lib vào thư mục lib đã tạo
Sau đó copy file opencv_world300.dll vào thư mục Debug của project.

Các bạn cấu hình thư viện OpenCV cho project như sau:



Bước 3: CvMFCImage class


Class CvMFCImage gồm có 2 file: CvMFCImage.h, CvMFCImage.cpp
Tiền thân của 2 anh em thằng này à lớp CvvImage của OpenCV 2.1.0. Từ phiên bản 2.2.0 trở về sau thì class này đã bị lưu đày biệt xứ :(.
Các bạn download 2 file này vào thư mực source code của project (link download ở cuối bài viết)

Sau đó các bạn Build project. Nếu build bị lỗi thì các bạn có thể tắt máy đi ngủ.

Bước 4: Thêm control vào Dialog để hiển thị ảnh


Trên thanh Menu, chọn View>Resource View. Click chọn IDD_MFCOPENCV_DIALOG

Các bạn chọn View>Toolbox, chọn Picture Control (hoặc Static Text cũng được).

Sau khi chọn thì click vào Dialog box để vẽ control đấy. Tiện tay thì sửa luôn cái Caption cho nút OK thành Browse.
Kết quả vẽ vời của tôi như hình bên dưới.

Tiếp theo, chúng ta sẽ viết code để hiển thị ảnh vào cái vùng hình nhật mà chúng ta vừa tạo xong.

Bước 5: Xử lý khi Click vào nút Browse.


Để tạo sự kiện click cho nút Browse, các bạn double click vào nút này. Visual Studio sẽ tự động tạo code như bên dưới:
void CMfcOpencvDlg::OnBnClickedOk()
{
    // TODO: Add your control notification handler code here
    CDialogEx::OnOK();
}
Các bạn sửa lại code như sau:
void CMfcOpencvDlg::OnBnClickedOk()
{
    CFileDialog fileDlg(TRUE, NULL, NULL, OFN_FILEMUSTEXIST, 
                        "(*.bmp;*.jpg;*.png)|*.png;*.jpg;*.png|", this);

    if(fileDlg.DoModal()==IDOK) {
        CString strPathname = fileDlg.GetPathName();
        IplImage *pImage = cvLoadImage(strPathname.GetBuffer(0),1);
        if(pImage) {
            ShowIplImage(pImage);
            cvReleaseImage(&pImage);
        } else {
            MessageBox(strPathname, "Open file error");
        }
    }
}
Tất nhiên để đoạn code này chạy được thì chúng ta cần phải implement hàm ShowIplImage() cho class CMfcOpencvDlg
Chúng ta sửa lại code cho class CMfcOpencvDlg như sau:
  • Khai báo buffer ảnh cho control (IplImage* m_pImgDisp)
  • Khai báo kích thước của buffer ảnh (IMG_WIDTH, IMG_HEIGHT)
  • Định nghĩa và implement cho hàm CopyImage()
  • Định nghĩa và implement cho hàm ShowIplImage()
// ...
#include "CvMFCImage.h"
// ...
class CMfcOpencvDlg : public CDialogEx
{
// Construction
public:
    CMfcOpencvDlg(CWnd* pParent = NULL);    // standard constructor
    ~CMfcOpencvDlg();

// ... -> Một số dòng code đã được ẩn đi :D
    static const unsigned int IMG_WIDTH  = 320; /* Chiều rộng của buffer ảnh */
    static const unsigned int IMG_HEIGHT = 240; /* Chiều cao của buffer ảnh */
// ...
protected:
    IplImage* m_pImgDisp; /* Buffer ảnh của control IDC_IMG_DSP */

    void CopyImage(const IplImage* imgI);
    void ShowIplImage(IplImage* pImgSrc);
// ...
public:
    afx_msg void OnBnClickedOk();
};
Buffer ảnh sẽ được cấp phát trong Constructor và thu hồi trong Destructor:
CMfcOpencvDlg::CMfcOpencvDlg(CWnd* pParent /*=NULL*/)
    : CDialogEx(CMfcOpencvDlg::IDD, pParent)
{
    m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
    m_pImgDisp = cvCreateImage(cvSize(IMG_WIDTH, IMG_HEIGHT),IPL_DEPTH_8U,3);
    cvSet((IplImage*)m_pImgDisp, CV_RGB(128,128,128));
}
CMfcOpencvDlg::~CMfcOpencvDlg()
{
    cvReleaseImage(&m_pImgDisp);
}
Hàm CopyImage() được implement như bên dưới:
/**
 * @brief Copy ảnh cần hiển thị vào buffer ảnh của control
 * @param pImg Ảnh cần hiển thị
 * @param imgContainer Ảnh 
 */
void CMfcOpencvDlg::CopyImage(const IplImage* img)
{
    float fXScale = 1.0;
    float fYScale = 1.0;
    float fScale = 1.0;
    int iDispWidth;
    int iDispHeight;
    int iDispWPad;
    int iDispHPad;

    /* Nếu ảnh cần hiển thị lớn hơn buffer ảnh thì cần phải thực hiện Rescale*/
    if(img->width > m_pImgDisp->width || img->height > m_pImgDisp->height) {
        fXScale = (float) ( (float)img->width / (float)m_pImgDisp->width );
        fYScale = (float) ( (float)img->height / (float)m_pImgDisp->height );
        /* Rescale theo chiều lớn hơn */
        if(fXScale > fYScale) {
            fScale = fXScale;
        } else {
            fScale = fYScale;
        }
    }

    /* Điều chỉnh để cho ảnh gốc được vẽ vào giữa buffer ảnh của control */
    iDispWidth    = (int)(img->width / fScale);
    iDispHeight = (int)(img->height / fScale);
    iDispWPad    = (m_pImgDisp->width - iDispWidth) / 2;
    iDispHPad    = (m_pImgDisp->height - iDispHeight) / 2;

    cvSet( m_pImgDisp, cvScalar(128,128,128) );
    cvSetImageROI( m_pImgDisp, cvRect(iDispWPad, iDispHPad, iDispWidth, iDispHeight) );
    cvResize( img, m_pImgDisp );
    cvResetImageROI( m_pImgDisp );
}
Hàm ShowIplImage():
/**
 * @brief Hiển thị ảnh lên Dialog control
 *
 * @param pImg Ảnh gốc cần hiển thị
 */
void CMfcOpencvDlg::ShowIplImage( IplImage* pImg )
{
    CDC* pDC = NULL;
    HDC hDC;
    CRect rect;

    /* Copy ảnh cần hiển thị vào buffer ảnh của control */
    CopyImage(pImg);

    pDC = GetDlgItem(IDC_IMG_DSP)->GetDC();
    hDC    = pDC->GetSafeHdc();
    GetDlgItem(IDC_IMG_DSP)->GetClientRect(&rect);

    /* Điều chỉnh để cho ảnh hiển thị giữa control */
    int rw = rect.right  - rect.left;
    int rh = rect.bottom - rect.top;
    int iw = m_pImgDisp->width;
    int ih = m_pImgDisp->height;
    int tx = (int)(rw - iw) / 2;
    int ty = (int)(rh - ih) / 2;
    SetRect( rect, tx, ty, tx + iw, ty + ih );

    /* Hiển thị ảnh lên control */
    CvMFCImage cimg;
    cimg.CopyOf( m_pImgDisp );
    cimg.DrawToHDC( hDC, &rect );

    ReleaseDC( pDC );
}
 

Kết luận

Như vậy là bằng việc sử dụng MFC Windows Form và class CvMFCImage chúng ta đã có được một ứng dụng trực quan cho việc demo kết quả nghiên cứu rồi. Đơn giản như đang giỡn :D

Source code

mfcopencv.zip

Tham khảo

http://opencv.org/
http://docs.opencv.org/trunk/doc/tutorials/introduction/windows_visual_studio_Opencv/windows_visual_studio_Opencv.html#windows-visual-studio-how-to

Thứ Ba, 25 tháng 2, 2014

An example of Pthread wait condition

#define _MULTI_THREADED
#include <stdio.h>
#include <time.h>
#include <pthread.h>

/* For safe condition variable usage, must use a boolean predicate and  */
/* a mutex with the condition.                                          */
int                 workToDo = 0;
pthread_cond_t      cond  = PTHREAD_COND_INITIALIZER;
pthread_mutex_t     mutex = PTHREAD_MUTEX_INITIALIZER;

#define NTHREADS                3
#define WAIT_TIME_SECONDS       15

void *threadfunc(void *parm)
{
  int               rc;
  struct timespec   ts;
  struct timeval    tp;

  rc = pthread_mutex_lock(&mutex);
  //check return code

  /* Usually worker threads will loop on these operations */
  while (1) {
    rc =  gettimeofday(&tp, NULL);

    /* Convert from timeval to timespec */
    ts.tv_sec  = tp.tv_sec;
    ts.tv_nsec = tp.tv_usec * 1000;
    ts.tv_sec += WAIT_TIME_SECONDS;

    while (!workToDo) {
      printf("Thread blocked\n");
      rc = pthread_cond_timedwait(&cond, &mutex, &ts);
      /* If the wait timed out, in this example, the work is complete, and   */
      /* the thread will end.                                                */
      /* In reality, a timeout must be accompanied by some sort of checking  */
      /* to see if the work is REALLY all complete. In the simple example    */
      /* we will just go belly up when we time out.                          */
      if (rc == ETIMEDOUT) {
        printf("Wait timed out!\n");
        rc = pthread_mutex_unlock(&mutex);
        pthread_exit(NULL);
      }
    }

    printf("Thread consumes work here\n");
    workToDo = 0;
  }

  rc = pthread_mutex_unlock(&mutex);
  return NULL;
}

int main(int argc, char **argv)
{
  int                   rc=0;
  int                   i;
  pthread_t             threadid[NTHREADS];

  printf("Enter Testcase - %s\n", argv[0]);

  printf("Create %d threads\n", NTHREADS);
  for(i=0; i<NTHREADS; ++i) {
    rc = pthread_create(&threadid[i], NULL, threadfunc, NULL);
    checkResults("pthread_create()\n", rc);
  }

  rc = pthread_mutex_lock(&mutex);

  printf("One work item to give to a thread\n");
  workToDo = 1;
  rc = pthread_cond_signal(&cond);

  rc = pthread_mutex_unlock(&mutex);

  printf("Wait for threads and cleanup\n");
  for (i=0; i<NTHREADS; ++i) {
    rc = pthread_join(threadid[i], NULL);
  }

  pthread_cond_destroy(&cond);
  pthread_mutex_destroy(&mutex);
  printf("Main completed\n");
  return 0;
}