2016年3月15日 星期二

OpenCV Get Pixel Value from Mat

OpenCV Get Pixel Value from Mat

在使用OpenCV時,經常會針對Mat裡的每一個pixel操作,可能是取值,也可能需要寫值。
但OpenCV提供了很多種方法可以做影像操作,如果有大量運算時,其實要找到一個比較適合且快速的方式。
從前使用OpenCV到現在也嘗試過很多種方式,今天稍微整理了幾種方式,做一個簡單的評估分享給大家。
我分類出較常用的影像取值6種方式:

測試方式

讀取一張512*512的影像,重複操作1000次,累加所有總計算時間。

方法 / 計算時間:

方法 運算時間
matControl_1 896ms
matControl_2 451ms
matControl_3 445ms
matControl_4 1410ms
matControl_5 449ms
matControl_6 880ms

matControl_1

使用Mat.ptr取出位址,再用[]方式取值

cv::Mat matControl_1( cv::Mat srcMat )
{
    cv::Mat dstMat( srcMat.rows, srcMat.cols, srcMat.type() );
    int nChannels = srcMat.channels();
    int nRows = srcMat.rows;
    int nCols = srcMat.cols;

    for( int j = 0; j < nRows; j++ ) {
        uchar* srcData = srcMat.ptr<uchar>(j);
        uchar* dstData = dstMat.ptr<uchar>(j);
        for( int i = 0; i < nCols; i++) {
            dstData[nChannels*i + 2]= srcData[nChannels*i + 2];
            dstData[nChannels*i + 1]= srcData[nChannels*i + 1];
            dstData[nChannels*i + 0]= srcData[nChannels*i + 0];
        }
    }
    return dstMat;
}

matControl_2

使用Mat.ptr取出位址,再用*方式取值

cv::Mat matControl_2( cv::Mat srcMat )
{
    cv::Mat dstMat( srcMat.rows, srcMat.cols, srcMat.type() );
    int nChannels = srcMat.channels();
    int nRows = srcMat.rows;
    int nCols = srcMat.cols * nChannels;

    for( int j = 0; j < nRows; j++ ){
        uchar* srcData= srcMat.ptr<uchar>(j);
        uchar* dstData = dstMat.ptr<uchar>(j);
        for( int i = 0; i < nCols; i++ ){
            *dstData++ = *srcData++;
        }
    }

    return dstMat;
}

matControl_3

直接指標取值

cv::Mat matControl_3( cv::Mat srcMat )
{

    cv::Mat dstMat( srcMat.rows, srcMat.cols, srcMat.type() );
    int nChannels = srcMat.channels();
    int nRows = srcMat.rows;
    int nCols = srcMat.cols * nChannels;
    int nStep = srcMat.step;

    uchar* srcData= srcMat.data;
    uchar* dstData = dstMat.data;
    for( int j = 0; j < nRows; j++ ){
        for( int i = 0; i < nCols; i++ ) {
            *(dstData+i) = *(srcData+i);
        }
        srcData += nStep;
        dstData += nStep;
    }

    return dstMat;
}

matControl_4

座標訪問

cv::Mat matControl_4( cv::Mat srcMat )
{
    cv::Mat dstMat( srcMat.rows, srcMat.cols, srcMat.type() );

    int nChannels = srcMat.channels();
    int nRows = srcMat.rows;
    int nCols = srcMat.cols * nChannels;
    for( int j = 0; j < nRows; j++ ){
        uchar* dstData = dstMat.ptr<uchar>(j);
        for( int i = 0; i < nCols; i++ ){
            cv::Vec3b srcPixel = srcMat.at<cv::Vec3b>(j, i);
            *dstData++ = srcPixel[0];
            *dstData++ = srcPixel[1];
            *dstData++ = srcPixel[2];
        }
    }

    return dstMat;
}

matControl_5

使用Mat.isContinuous()判斷是否為連續影像,優化迴圈

cv::Mat matControl_5( cv::Mat srcMat )
{
    cv::Mat dstMat( srcMat.rows, srcMat.cols, srcMat.type() );
    int nChannels = srcMat.channels();
    int nRows = srcMat.rows;
    int nCols = srcMat.cols * nChannels;

    if( srcMat.isContinuous()){
        nCols *= nRows;
        nRows = 1;
    }

    for( int j = 0; j < nRows; j++ ){
        uchar* srcData= srcMat.ptr<uchar>(j);
        uchar* dstData = dstMat.ptr<uchar>(j);
        for( int i = 0; i < nCols; i++ ){
            *dstData++ = *srcData++;
        }
    }
    return dstMat;
}

matControl_6

使用iterator訪問取值

cv::Mat matControl_6( cv::Mat srcMat )
{
    cv::Mat dstMat( srcMat.rows, srcMat.cols, srcMat.type() );
    cv::Mat_<cv::Vec3b>::iterator it = srcMat.begin<cv::Vec3b>();
    cv::Mat_<cv::Vec3b>::iterator itend = srcMat.end<cv::Vec3b>();
    cv::Mat_<cv::Vec3b>::iterator dstIter = dstMat.begin<cv::Vec3b>();

    for ( ; it!= itend; ++it, ++dstIter) {
        (*dstIter)[0]= (*it)[0];
        (*dstIter)[1]= (*it)[1];
        (*dstIter)[2]= (*it)[2];
    }

    return dstMat;
}

更多OpenCV文章請參考:OpenCV Tutorial (學習筆記)

0 意見:

張貼留言