OpenCV學習C++接口:圖像遍歷+像素壓縮

學習體會:ios

  1. 當Mat爲多通道時,如3通道,若是咱們將其內容輸出到終端,則能夠看出其列數爲Mat::colsn倍,固然nMat的通道數。雖是如此,可是Mat::cols的數值並無隨之改變。
  2. 當複製一副圖像時,利用函數cv::Mat::clone(),則將在內存中從新開闢一段新的內存存放複製的圖像(圖像數據也將所有複製),而若是利用cv::Mat::copyTo()複製圖像,則不會在內存中開闢一段新的內存塊,同時也不會複製圖像數據,複製先後的圖像的指針指向同一個內存塊。使用的時候需注意兩個函數的區別。
  3. 爲了不函數參數傳遞時出現複製狀況,函數的形參多采用傳遞reference,如cv::Mat &image,傳遞輸入圖像的引用,不過這樣函數可能會對輸入圖像進行修改,並反映到輸出結果上;若是想避免修改輸入圖像,則函數形參可傳遞const reference,這樣輸入圖像不會被修改,同時能夠建立一個輸出圖像Mat,將函數處理的結果保存到輸出圖像Mat中,例如:void colorReduce4(const cv::Mat &image, cv::Mat &result,int div = 64)。
  4. 採用迭代器iterator來遍歷圖像像素,可簡化過程,比較安全,不過效率較低;若是想避免修改輸入圖像實例cv::Mat,可採用const_iterator
  5. 遍歷圖像時,不要採用.at()方式,這種效率最低。
  6. 進行圖像像素壓縮時,利用位操做的算法效率最高,其次是利用整數除法中向下取整,效率最低的是取模運算。
  7. 設計函數時,須要檢查計算效率來提升程序的性能,不過以犧牲程序的可讀性來提升代碼執行的效率並非一個明智的選擇。
  8. 執行效率狀況見程序運行結果。
/***************************************************************
*
*    內容摘要:本例採用8種方法對圖像Mat的像素進行掃描,並對像素點的像
*            素進行壓縮,壓縮間隔爲div=64,並比較掃描及壓縮的效率,效
*            率最高的是採用.ptr及減小循環次數來遍歷圖像,並採用位操
*            做來對圖像像素進行壓縮。
*   做    者:Jacky Liu
*   完成日期:2012.8.10
*   參考資料:《OpenCV 2 computer Vision Application Programming
*              cookbook》
*
***************************************************************/
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>


//利用.ptr和數組下標進行圖像像素遍歷
void colorReduce0(cv::Mat &image, int div = 64)
{
    int nl = image.rows;
    int nc = image.cols * image.channels();
    
    //遍歷圖像的每一個像素
    for(int j=0; j<nl ;++j)
    {
        uchar *data = image.ptr<uchar>(j);
        for(int i=0; i<nc; ++i)
        {
            data[i] = data[i]/div*div+div/2;     //減小圖像中顏色總數的關鍵算法:if div = 64, then the total number of colors is 4x4x4;整數除法時,是向下取整。
        }
    }
}


//利用.ptr和 *++ 進行圖像像素遍歷
void colorReduce1(cv::Mat &image, int div = 64)
{
    int nl = image.rows;
    int nc = image.cols * image.channels();
    
    //遍歷圖像的每一個像素
    for(int j=0; j<nl ;++j)
    {
        uchar *data = image.ptr<uchar>(j);
        for(int i=0; i<nc; ++i)
        {
            *data++ = *data/div*div + div/2;
        }
    }
}


//利用.ptr和數組下標進行圖像像素遍歷,取模運算用於減小圖像顏色總數
void colorReduce2(cv::Mat &image, int div = 64)
{
    int nl = image.rows;
    int nc = image.cols * image.channels();
    
    //遍歷圖像的每一個像素
    for(int j=0; j<nl ;++j)
    {
        uchar *data = image.ptr<uchar>(j);
        for(int i=0; i<nc; ++i)
        {
            data[i] = data[i]-data[i]%div +div/2;  //利用取模運算,速度變慢,由於要讀每一個像素兩次
        }
    }
}

//利用.ptr和數組下標進行圖像像素遍歷,位操做運算用於減小圖像顏色總數
void colorReduce3(cv::Mat &image, int div = 64)
{
    int nl = image.rows;
    int nc = image.cols * image.channels();

    int n = static_cast<int>(log(static_cast<double>(div))/log(2.0));   //div=64, n=6
    uchar mask = 0xFF<<n;                                            //e.g. div=64, mask=0xC0
    
    //遍歷圖像的每一個像素
    for(int j=0; j<nl ;++j)
    {
        uchar *data = image.ptr<uchar>(j);
        for(int i=0; i<nc; ++i)
        {
            *data++ = *data&mask + div/2;
        }
    }
}

//形參傳入const conference,故輸入圖像不會被修改;利用.ptr和數組下標進行圖像像素遍歷
void colorReduce4(const cv::Mat &image, cv::Mat &result,int div = 64)
{
    int nl = image.rows;
    int nc = image.cols * image.channels();

    result.create(image.rows,image.cols,image.type());
    
    //遍歷圖像的每一個像素
    for(int j=0; j<nl ;++j)
    {
        const uchar *data_in = image.ptr<uchar>(j);
        uchar *data_out = result.ptr<uchar>(j);
        for(int i=0; i<nc; ++i)
        {
            data_out[i] = data_in[i]/div*div+div/2;     //減小圖像中顏色總數的關鍵算法:if div = 64, then the total number of colors is 4x4x4;整數除法時,是向下取整。
        }
    }
}

//利用.ptr和數組下標進行圖像像素遍歷,並將nc放入for循環中(比較糟糕的作法)
void colorReduce5(cv::Mat &image, int div = 64)
{
    int nl = image.rows;
    
    //遍歷圖像的每一個像素
    for(int j=0; j<nl ;++j)
    {
        uchar *data = image.ptr<uchar>(j);
        for(int i=0; i<image.cols * image.channels(); ++i)
        {
            data[i] = data[i]/div*div+div/2;     //減小圖像中顏色總數的關鍵算法:if div = 64, then the total number of colors is 4x4x4;整數除法時,是向下取整。
        }
    }
}

//利用迭代器 cv::Mat iterator 進行圖像像素遍歷
void colorReduce6(cv::Mat &image, int div = 64)
{
    cv::Mat_<cv::Vec3b>::iterator it = image.begin<cv::Vec3b>();    //因爲利用圖像迭代器處理圖像像素,所以返回類型必須在編譯時知道
    cv::Mat_<cv::Vec3b>::iterator itend = image.end<cv::Vec3b>();

    for(;it != itend; ++it)
    {
        (*it)[0] = (*it)[0]/div*div+div/2;        //利用operator[]處理每一個通道的像素
        (*it)[1] = (*it)[1]/div*div+div/2;
        (*it)[2] = (*it)[2]/div*div+div/2;
    }
}

//利用.at<cv::Vec3b>(j,i)進行圖像像素遍歷
void colorReduce7(cv::Mat &image, int div = 64)
{
    int nl = image.rows;
    int nc = image.cols;
    
    //遍歷圖像的每一個像素
    for(int j=0; j<nl ;++j)
    {
        for(int i=0; i<nc; ++i)
        {
            image.at<cv::Vec3b>(j,i)[0] = image.at<cv::Vec3b>(j,i)[0]/div*div + div/2;
            image.at<cv::Vec3b>(j,i)[1] = image.at<cv::Vec3b>(j,i)[1]/div*div + div/2;
            image.at<cv::Vec3b>(j,i)[2] = image.at<cv::Vec3b>(j,i)[2]/div*div + div/2;
        }
    }
}

//減小循環次數,進行圖像像素遍歷,調用函數較少,效率最高。
void colorReduce8(cv::Mat &image, int div = 64)
{
    int nl = image.rows;
    int nc = image.cols;

    //判斷是不是連續圖像,便是否有像素填充
    if(image.isContinuous())
    {
        nc = nc*nl;
        nl = 1;
    }

    int n = static_cast<int>(log(static_cast<double>(div))/log(2.0));
    uchar mask = 0xFF<<n;
    
    //遍歷圖像的每一個像素
    for(int j=0; j<nl ;++j)
    {
        uchar *data = image.ptr<uchar>(j);
        for(int i=0; i<nc; ++i)
        {
            *data++ = *data & mask +div/2;
            *data++ = *data & mask +div/2;
            *data++ = *data & mask +div/2;
        }
    }
}

const int NumTests = 9;        //測試算法的數量
const int NumIteration = 20;   //迭代次數

int main(int argc, char* argv[])
{
    int64 t[NumTests],tinit;
    cv::Mat image1;
    cv::Mat image2;
    
    //數組初始化
    int i=0;
    while(i<NumTests)
    {
        t[i++] = 0;
    }

    int n = NumIteration;
    
    //迭代n次,取平均數
    for(int i=0; i<n; ++i)
    {
        image1 = cv::imread("../boldt.jpg");

        if(!image1.data)
        {
            std::cout<<"read image failue!"<<std::endl;
            return -1;
        }

        // using .ptr and []
        tinit = cv::getTickCount();
        colorReduce0(image1);
        t[0] += cv::getTickCount() - tinit;
        
        // using .ptr and *++
        image1 = cv::imread("../boldt.jpg");
        tinit = cv::getTickCount();
        colorReduce1(image1);
        t[1] += cv::getTickCount()  - tinit;
        
        // using .ptr and [] and modulo
        image1 = cv::imread("../boldt.jpg");
        tinit = cv::getTickCount();
        colorReduce2(image1);
        t[2] += cv::getTickCount()  - tinit;
        
        // using .ptr and *++ and bitwise
        image1 = cv::imread("../boldt.jpg");
        tinit = cv::getTickCount();
        colorReduce3(image1);
        t[3] += cv::getTickCount()  - tinit;

        //using input and output image
        image1 = cv::imread("../boldt.jpg");
        tinit = cv::getTickCount();
        colorReduce4(image1,image2);
        t[4] += cv::getTickCount()  - tinit;
        
        // using .ptr and [] with image.cols * image.channels()
        image1 = cv::imread("../boldt.jpg");
        tinit = cv::getTickCount();
        colorReduce5(image1);
        t[5] += cv::getTickCount()  - tinit;
        
        // using .ptr and *++ and iterator
        image1 = cv::imread("../boldt.jpg");
        tinit = cv::getTickCount();
        colorReduce6(image1);
        t[6] += cv::getTickCount()  - tinit;
        
        //using at
        image1 = cv::imread("../boldt.jpg");
        tinit = cv::getTickCount();
        colorReduce7(image1);
        t[7] += cv::getTickCount()  - tinit;

        //using .ptr and * ++ and bitwise (continuous+channels)
        image1 = cv::imread("../boldt.jpg");
        tinit = cv::getTickCount();
        colorReduce8(image1);
        t[8] += cv::getTickCount()  - tinit;
    }

    cv::namedWindow("Result");
    cv::imshow("Result",image1);
    cv::namedWindow("Result Image");
    cv::imshow("Result Image",image2);

    std::cout<<std::endl<<"-------------------------------------------------------------------------"<<std::endl<<std::endl;
    std::cout<<"using .ptr and [] = "<<1000*t[0]/cv::getTickFrequency()/n<<"ms"<<std::endl;
    std::cout<<"using .ptr and *++ = "<<1000*t[1]/cv::getTickFrequency()/n<<"ms"<<std::endl;
    std::cout<<"using .ptr and [] and modulo = "<<1000*t[2]/cv::getTickFrequency()/n<<"ms"<<std::endl;
    std::cout<<"using .ptr and *++ and bitwise = "<<1000*t[3]/cv::getTickFrequency()/n<<"ms"<<std::endl;
    std::cout<<"using input and output image = "<<1000*t[4]/cv::getTickFrequency()/n<<"ms"<<std::endl;
    std::cout<<"using .ptr and [] with image.cols * image.channels() = "<<1000*t[5]/cv::getTickFrequency()/n<<"ms"<<std::endl;
    std::cout<<"using .ptr and *++ and iterator = "<<1000*t[6]/cv::getTickFrequency()/n<<"ms"<<std::endl;
    std::cout<<"using at = "<<1000*t[7]/cv::getTickFrequency()/n<<"ms"<<std::endl;
    std::cout<<"using .ptr and * ++ and bitwise (continuous+channels) = "<<1000*t[8]/cv::getTickFrequency()/n<<"ms"<<std::endl;
    std::cout<<std::endl<<"-------------------------------------------------------------------------"<<std::endl<<std::endl;
    cv::waitKey();
    return 0;
}
相關文章
相關標籤/搜索