Top Ad unit 728 × 90

Latest news

recent

Các phương pháp truy xuất pixel dùng OpenCV

Ảnh được cấu tạo bởi các pixel, điểm ảnh, chính vì lẽ đó mà có thể nói xử lý ảnh là quá trình xử lý trên điểm ảnh. Làm sao để làm việc một cách hiệu quả, truy xuất, cập nhật nhanh nhất, thuận tiện nhất các giá trị này là vấn đề căn cơ cần phải học khi nhập môn.
Giả sử bạn cần truy xuất vào kênh màu thứ K của điểm ảnh ở dòng i và cột thứ j. Chỉ mục i của dòng thuộc vào khoảng [0, height - 1]. Chỉ mục j của cột nằm trong đoạn [0, width-1]. Chỉ mục K của kênh màu thì giới hạn trong [0, nchannels - 1].Tôi đưa ra các thông tin khoảng chạy này với ngụ ý nếu bạn sử dụng các phương pháp dưới đây trong vòng lặp thích hợp bạn có thể duyệt toàn các điểm trong ảnh.
  • Phương pháp truy xuất gián tiếp (Indirect access): (đánh giá: thông dụng, không hiệu quả, truy xuất đến bất kì định dạng nào được hỗ trợ bởi OpenCV)
    • Dùng cho hình chỉ có 1byte / 1 kênh màu (single-channel byte image):
      IplImage* img=cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,1);
      CvScalar s;
      s=cvGet2D(img,i,j); // get the (i,j) pixel value
      printf("intensity=%f\n",s.val[0]);
      s.val[0]=111;
      cvSet2D(img,i,j,s); // set the (i,j) pixel value
    • Dùng cho hình đa kênh với mỗi kênh là 1 byte hay 1 float (multi-channel float (or byte) image):
      IplImage* img=cvCreateImage(cvSize(640,480),IPL_DEPTH_32F,3);
      CvScalar s;
      s=cvGet2D(img,i,j); // get the (i,j) pixel value
      printf("B=%f, G=%f, R=%f\n",s.val[0],s.val[1],s.val[2]);
      s.val[0]=111;
      s.val[1]=111;
      s.val[2]=111;
      cvSet2D(img,i,j,s); // set the (i,j) pixel value
  • Truy xuất trực tiếp: (Hiệu quả, dễ tìm ẩn lỗi)
    • Dùng cho single-channel byte image:
      IplImage* img=cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,1);
      ((uchar *)(img->imageData + i*img->widthStep))[j]=111;
    • Dùng cho multi-channel byte image:
      IplImage* img=cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,3);
      ((uchar *)(img->imageData + i*img->widthStep))[j*img->nChannels + 0]=111; // B
      ((uchar *)(img->imageData + i*img->widthStep))[j*img->nChannels + 1]=112; // G
      ((uchar *)(img->imageData + i*img->widthStep))[j*img->nChannels + 2]=113; // R
    • Dùng cho multi-channel float image:
      IplImage* img=cvCreateImage(cvSize(640,480),IPL_DEPTH_32F,3);
      ((float *)(img->imageData + i*img->widthStep))[j*img->nChannels + 0]=111; // B
      ((float *)(img->imageData + i*img->widthStep))[j*img->nChannels + 1]=112; // G
      ((float *)(img->imageData + i*img->widthStep))[j*img->nChannels + 2]=113; // R
  • Truy xuất trực tiếp sử dụng pointer: (Đơn giản, hiệu quả)
    • Dùng cho single-channel byte image:
      IplImage* img  = cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,1);
      int height     = img->height;
      int width      = img->width;
      int step       = img->widthStep/sizeof(uchar);
      uchar* data    = (uchar *)img->imageData;
      data[i*step+j] = 111;
    • Dùng cho multi-channel byte image:
      IplImage* img  = cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,3);
      int height     = img->height;
      int width      = img->width;
      int step       = img->widthStep/sizeof(uchar);
      int channels   = img->nChannels;
      uchar* data    = (uchar *)img->imageData;
      data[i*step+j*channels+k] = 111;
    • Dùng cho multi-channel float image (assuming a 4-byte alignment):
      IplImage* img  = cvCreateImage(cvSize(640,480),IPL_DEPTH_32F,3);
      int height     = img->height;
      int width      = img->width;
      int step       = img->widthStep/sizeof(float);
      int channels   = img->nChannels;
      float * data    = (float *)img->imageData;
      data[i*step+j*channels+k] = 111;
  • Truy xuất trực tiếp dùng c++ wrapper: (Đơn giản và truy xuất hiệu quả)
    • Định nghĩa a c++ wrapper cho single-channel byte images, multi-channel byte images, and multi-channel float images:
      template class Image
      {
        private:
        IplImage* imgp;
        public:
        Image(IplImage* img=0) {imgp=img;}
        ~Image(){imgp=0;}
        void operator=(IplImage* img) {imgp=img;}
        inline T* operator[](const int rowIndx) {
          return ((T *)(imgp->imageData + rowIndx*imgp->widthStep));}
      };
      
      typedef struct{
        unsigned char b,g,r;
      } RgbPixel;
      
      typedef struct{
        float b,g,r;
      } RgbPixelFloat;
      
      typedef Image       RgbImage;
      typedef Image  RgbImageFloat;
      typedef Image  BwImage;
      typedef Image          BwImageFloat;
    • Đối với single-channel byte image:
      IplImage* img=cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,1);
      BwImage imgA(img);
      imgA[i][j] = 111;
    • Đối với multi-channel byte image:
      IplImage* img=cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,3);
      RgbImage  imgA(img);
      imgA[i][j].b = 111;
      imgA[i][j].g = 111;
      imgA[i][j].r = 111;
    • Đối với multi-channel float image:
      IplImage* img=cvCreateImage(cvSize(640,480),IPL_DEPTH_32F,3);
      RgbImageFloat imgA(img);
      imgA[i][j].b = 111;
      imgA[i][j].g = 111;
      imgA[i][j].r = 111;
  • BONUS: Ứng dụng phương pháp truy xuất để duyệt image data
void VnPPixelAccess(IplImage *image) {
      int nl= image->height; // number of lines
      int nc= image->width * image->nChannels; // total number of element per line
      int step= image->widthStep; // effective width
      // get the pointer to the image buffer
      unsigned char *data= reinterpret_cast (image->imageData);
      for (int i=0; i < nl; i++) {
            for (int j=0; j < nc; j += nChannels) {
            // process each pixel ---------------------
                  for(int k=0; k < image->nChannels; k++) {
                         data[j + k]= giá trị mà bạn muốn :p;                       
                  }
            // end of pixel processing ----------------
            } // end of line
                  
            data+= step;  // next line
      }
}



Binh Nguyen - Bioz
Các phương pháp truy xuất pixel dùng OpenCV Reviewed by Bioz Nguyen on 8:11:00 AM Rating: 5

12 comments:

  1. Góp thêm chút kinh nghiệm đối với Emgu(OpenCV trên nền tảng .Net, ngôn ngữ C#):
    Emgu có lớp Image, mô tả ảnh. Có thể ảnh màu hoặc ảnh xám. Một phương pháp để truy xuất giá trị điểm ảnh đối với điểm ảnh này là dùng thuộc tính Intensity, Chẳng hạn với ảnh xám Image Img, Img[i,j].Intensity là giá trị điểm ảnh tại tọa độ (i,j).
    Với phương thức truy xuất này, mặc dù chỉ mới một vài thao tác trừ ảnh, xử lý sơ sơ thôi mà nó chạy chậm rì chật rịt, giật liên hồi. Lúc đó cứ thấy 2 vòng for lồng nhau là run tay không giám gõ. Thế là đi tìm các cải tiến mấy cái giải thuật của mình.
    Sau mấy đợt cải ngược cải xuôi mới mò thử và ra một cách khác: Img[i,j,0] == Img[i,j].Intensity.
    Thì ra trong tổ chức bộ nhớ của anh Emgu, mỗi ảnh là một ma trận 3 chiều, với ảnh màu RGB, nó có các giá trị Img[i,j,0], Img[i,j,1] và Img[i,j,2] tương ứng các giá trị R,G,B của tọa độ (i,j) (!có thể nhầm thứ tự RGB).

    Cách truy xuất trực tiếp vào bộ nhớ như thế này cho thời gian giảm xuống một cách rất đáng kể. (Cực kì quan trọng đối với các hệ thống chạy theo thời gian thực). Mình chưa do chính xác xem khoảng chênh lệch là bao nhiêu (tương lai sẽ đo), tuy nhiên cũng cảm nhận được nó tăng lên ít nhất là 2 lần. Rất đã!

    ReplyDelete
  2. Tương lai của xử lý thời gian thực sẽ là hệ thống nhúng, chính vì lẽ đó mà C và con trỏ luôn có sự nổi trội trong việc xây dựng các ứng dụng này. Kiến thức về quản lý và truy xuất trực tiếp trên vùng nhớ là hết sức quan trọng trong việc tăng tốc xử lý, đó chính là lý do mà IEEV muốn đầu tư vào thư viện C và kèm theo một bộ C# wrapper. Những gì bạn thấy ở Emgu thực tế vẫn còn quá phức tạp, nó chỉ nhanh vì máy bạn đang dùng là desktop với CPU lên tới 3GHz, bạn hãy thử dùng Emgu để xử lý 5 stream video cùng lúc bạn sẽ thấy máy của bạn lết như thế nào :). ANW đây là thu viện tốt, tuy nhiên ngày nay việc xử lý video 1 stream trên desktop ko còn là mục tiêu, cái cần là xử lý multi stream cho máy trạm hay xử lý video trên thiết bị nhúng như IP camera ...

    ReplyDelete
  3. Cho hỏi chút làm sao tích hợp OpenCV trong môi trường MSVS 2010.
    Mình đang thử để dùng nó mà chưa được.
    Trước đây có lần dùng OpenC cũng của Intel thì có một số thiết tập trong trình soạn thảo thì soạn thảo và compiler và chạy Ok.
    Mong IEEV chỉ dùm.

    ReplyDelete
  4. :) tôi cũng chỉ mới cài VS2010 chứ chưa đụng tới nó. Tuy nhiên theo tôi biết sự khác biệt của vs2010 là nó sử dụng XML profile để config preprocessing cho compiler và linker chứ không còn giống 2008 nữa. Sẽ thử trong thời gian gần và trả lời cho bạn.

    ReplyDelete
  5. Hiện trong phần download sản phẩm của IEEV đã có 1 dự án về tập hợp các ví dụ được Bioz thiết kế chuẩn dành cho các ứng dụng dùng opencv xử lý ảnh và video, vui lòng tham khảo và rút ra bài học cho mình từ đó cũng như đóng góp phát triển thêm.

    ReplyDelete
  6. chào anh,

    Em đang tìm hiểu về phần này, nếu có 2 hình A và B , nếu trong B có A thì có thể tìm A trong B theo cách nào ạ ? Có phải ta duyệt theo cột và dòng của B nếu thấy data gần trùng khớp thì cout << "Tìm ra" , cách này có được không ạ ?

    Mong sớm nhận được hồi âm của anh

    ReplyDelete
  7. vấn đề của em người ta gọi là template matching, có thể đọc bài viết sau để hiểu thêm.

    www.ieev.org/2009/06/template-matching.html

    ReplyDelete
  8. Tôi đang tìm hiểu phần mềm VnPLIp của bạn nhưng khi chạy ứng dụng thì chỉ thấy hiện ra một cửa sổ cho phép load ảnh mà ko thấy có các chức năng xử lý gì. Mong bạn chỉ giúp!
    Thanks,
    Trung

    ReplyDelete
  9. lần sau bạn vui lòng nói rỏ tên đầy đủ ví dụ bạn đang chạy. Vui lòng đọc description trong khu vực download, sau khi compile thì chịu khó đọc source code trước khi hỏi. Chúng tôi chỉ gợi mở và cho người đọc điểm xuất phát cùng định hướng chứ ko có ý định đi xa hơn thế.

    ReplyDelete
  10. hi!
    mình có 1 bài toán là: đưa vào một ảnh, cân bằng màu sắc cho nó(color balance) bằng opencv, pro nào có code júp mình với

    ReplyDelete
  11. http://www.ieev.org/2009/09/learning-computer-vision-with-opencv.html
    bạn có thể tham khảo tài liệu này để tìm ra hàm thích hợp nếu có.

    ReplyDelete
  12. anh có thể viết và chú thích những chỗ code được không. Em mới học và đọc hơi khó hiểu nhất là những chỗ i*step và +k tự nhiên chả biết ở đâu ra luôn.

    ReplyDelete

chia sẻ cho chúng tôi ý tưởng hay khó khăn của bạn ...

All Rights Reserved by IEEV © 2009 - 2016
Powered By Blogger, Designed by Sweetheme

Contact Form

Name

Email *

Message *

Powered by Blogger.