1. 程式人生 > >讀取16bit影象資料小結

讀取16bit影象資料小結

opencv2 讀取16bit影象資料小結

https://blog.csdn.net/zhyh1435589631/article/details/49275945

今天師弟跑來問我一個關於opencv影象值讀取的問題, 原話是這樣的

在opencv中想要載入一幅16位的影象,滑鼠點選顯示其位置和灰度數值,但是我寫的程式中灰度數值最多隻能顯示255

拿到這個問題, 自己也不是很清楚, 因為自己其實也是剛剛才接觸opencv2.x, 雖然已經出來3了,anyway, 總是要裝下逼的, 於是就讓他把程式碼拿了過來, 打算自己除錯一下。

本來想試試qt除錯, 結果整了半天都沒停到斷點處, 只好換成了vs。 
經過除錯發現:

  1. 一開始我們以為是資料載人有問題, 結果發現數據載入進來的確是 CV_16U型別的, 單通道灰度圖。 imread 的引數 CV_LOAD_IMAGE_UNCHANGED 和 CV_LOAD_IMAGE_ANYCOLOR | CV_LOAD_IMAGE_ANYDEPTH 效果是一致的
  2. 我們試圖用 Mat 的 at 方法讀取資料, 由於at 是個模板方法, 試了一下發現 uchar 不行, Vec2b 也不行(後來發現, Vec2b 使用在 2 通道的情況底下的), 顯然CV_16U 對應的型別應該是 2 個位元組, 故而用 ushort 型別, 正好滿足要求。 
    至此, 已經能夠滿足讀取灰度值的要求了。
auto it = img.at<ushort>(y, x);
  • 1

3.師弟的程式中用了

        uchar * data = img.data;
        // img.at(i, j)
        data = img.data + pt.y*img.step + pt.x*img.elemSize();
        int intgray = (*data);
  • 1
  • 2
  • 3
  • 4

來獲取灰度值, 顯然是不正確的, 因為每個元素是 CV_16U 型別, ie, 每個影象資料佔 2 個位元組, 而這裡僅僅只是訪問到了該位置影象資料的第一個部分的資料, 前1個位元組, 還剩下一個位元組沒有讀取!!!

一般我們的intel cpu x86 x64 大多都是使用小端模式儲存的, ie, 低地址放低位元組, 高地址放高位元組。 
在我們現在這個環境中, 高位元組部分沒有讀取, 因而需要加上高位元組部分, 同時需要注意 C++ 的 運算子優先順序, 優先順序

′+′>′<<′′+′>′<<′


因而 data2 << 8 需要加上括號!!! 
如下所示:

 

        uchar * data = img.data;
        // img.at(i, j)
        data = img.data + pt.y*img.step + pt.x*img.elemSize();
        uchar data2 = *(data + 1);
        int intgray = (*data) + (data2 << 8);
  • 1
  • 2
  • 3
  • 4
  • 5

4.注意opencv影象表示中是按照行列來表示的, 影象座標系處於影象的左上角, 而我們一般認為的 x, y 笛卡爾座標系的原點是位於 影象的左下角, 通過 at 方法讀取資料的時候, 要注意第一個引數應該表示影象的行, 第二個引數才是表示影象的列。

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include<opencv.hpp>
#include <opencv\highgui.h>
using namespace cv;
using namespace std;

Mat img; //影象
const string lpwindowname = "影象09230021";

const string lpImageName = R"(E:\system dir\Desktop\QT\opencv2 computer vision\Opencv2\tmp\pixel\09230021.tif)";
void on_mousemove(int event, int x, int y, int flags, void* param);
int mid(int i, int a, int b);

int main()
{
    img = imread(lpImageName, CV_LOAD_IMAGE_UNCHANGED);//載入影象
    namedWindow(lpwindowname, 0);//建立視窗
    imshow(lpwindowname, img);//在已經建立的視窗中顯示影象
    setMouseCallback(lpwindowname, on_mousemove, 0);//響應滑鼠事件
    waitKey(0);
    destroyAllWindows();
    return 0;
}

int mid(int i, int a, int b)
{
    return min(max(i, min(a, b)), max(a, b));
}

void on_mousemove(int event, int x, int y, int flags, void* param)
{
    //Font font;
    //cvinitFont(&font, CV_FONT_HERSHEY_DUPLEX, 1, 1, 1, 1, CV_AA);
    int width = img.cols;//圖片寬度
    int height = img.rows;//圖片高度
    Point text_pt;
    Size text_size;
    int baseline;
    if (event == CV_EVENT_LBUTTONDOWN)
    {
        x = mid(x, 0, img.cols);
        y = mid(y, 0, img.rows);

//      auto tmp = img.type();
//      auto tmp2 = img.depth();

        Point pt = Point(x, y);
        int  aaa = img.channels();
        uchar * data = img.data;
        // img.at(i, j)
        data = img.data + pt.y*img.step + pt.x*img.elemSize();
        uchar data2 = *(data + 1);
        int intgray = (*data) + (data2 << 8);

        auto a2 = img.at<ushort>(y, x);

        // ************* 後面就沒除錯了 ******************

        char site[100];
        sprintf(site, "(%d,%d)%d", pt.x, pt.y, intgray);
        circle(img, pt, 2, Scalar(255, 255, 255), 1, CV_AA, 0);
        text_size = getTextSize(site, CV_FONT_HERSHEY_DUPLEX, 1, 1, &baseline);
        text_pt.x = mid(pt.x, 0, width - text_size.width);
        text_pt.y = mid(pt.y, text_size.height + baseline, height);
        putText(img, site, text_pt, CV_FONT_HERSHEY_DUPLEX, 1, Scalar(255,255,255), 1, CV_AA);
        imshow(lpwindowname, img);
        img = imread(lpImageName, CV_LOAD_IMAGE_UNCHANGED);//載入影象

    }
}