opencv的畫素遍歷和基本的矩陣處理
阿新 • • 發佈:2019-01-23
前言
opencv的所有資料都是以一個mat儲存的,可是我們需要對各個畫素處理,這裡必須高效的對畫素快速的迴圈遍歷,而矩陣對於畫素的處理也具有得天獨厚的優勢。在這一篇部落格中我們慢慢的學習一下。
正文
對於mat的迴圈便利也比較簡單我們首先最容易想到的方法是:
Mat& ScanImageAndReduceC(Mat& I)
{
// accept only char type matrices
CV_Assert(I.depth() == CV_8U);
int channels = I.channels();
int nRows = I.rows;
int nCols = I.cols * channels;
if (I.isContinuous())
{
nCols *= nRows;
nRows = 1;
}
int i,j;
uchar* p;
for( i = 0; i < nRows; ++i)
{
p = I.ptr<uchar>(i);
for ( j = 0; j < nCols; ++j)
{
p[j] = table[p[j]];
}
}
return I;
}
但是如何使用呢我們看下主程式
#include<iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
using namespace cv;
Mat& ScanImageAndReduceC(Mat& I)
{
// accept only char type matrices
CV_Assert(I.depth() == CV_8U);
int channels = I.channels();
int nRows = I.rows;
int nCols = I.cols * channels;
if (I.isContinuous())
{
nCols *= nRows;
nRows = 1;
}
int i,j;
uchar* p;
for( i = 0; i < nRows; ++i)
{
p = I.ptr<uchar>(i);
for ( j = 0; j < nCols; ++j)
{
p[j] = p[j]/4;
}
}
return I;
}
int main()
{
// 讀入一張圖片(遊戲原畫)
Mat img=imread("timg.jpeg");
ScanImageAndReduceC(img);
// 建立一個名為 "遊戲原畫"視窗
namedWindow("遊戲原畫");
// 在視窗中顯示遊戲原畫
imshow("遊戲原畫",img);
// 等待6000 ms後窗口自動關閉
waitKey(6000);
}
這種明顯效率更高我們下面看下迭代器方法:
Mat& ScanImageAndReduceC(Mat& I)
{
// accept only char type matrices
CV_Assert(I.depth() == CV_8U);
const int channels = I.channels();
switch(channels)
{
case 1:
{
MatIterator_<uchar> it, end;
for( it = I.begin<uchar>(), end = I.end<uchar>(); it != end; ++it)
*it = table[*it];
break;
}
case 3:
{
MatIterator_<Vec3b> it, end;
for( it = I.begin<Vec3b>(), end = I.end<Vec3b>(); it != end; ++it)
{
(*it)[0] = (*it)[0]/4;
(*it)[1] = (*it)[1]/4;
(*it)[2] = (*it)[2]/4;
}
}
}
return I;
}
這個效率明顯比較第,下面我們看下一個更高效率的
void ScanImageAndReduceC(Mat& I)
{
Mat lookUpTable(1, 256, CV_8U);
uchar* p = lookUpTable.ptr();
for( int i = 0; i < 256; ++i)
p[i] = i/4;
LUT(img, lookUpTable, img);
return;
}
這個效率最高,是opencv自帶的一個迴圈替換的函式,這裡就不在詳細介紹,大家可以自己查閱具體用法。
我們如果需要把畫素中的過渡很尖銳的部分給平均掉,圖片不會那麼尖銳。影象處理很關鍵的過影象濾波都需要對畫素通過卷積進行掩膜(mask)操作,其實相當於加了一層濾鏡。下面看一個操作
這就是最簡單的一個降噪的一個方法。這就是將中間的畫素減去周圍四個點畫素至。操作很簡單,關鍵下面是一個卷積計算,公式這裡就不詳細介紹,這就是矩陣的卷積運算,
我們看下這個加上濾鏡的效果
忽然會發現沒愛了,為啥我的處理吧圖片變得模糊了呢,這裡就是那個運算元有問題啦,這裡我暫時沒詳細看這部分的演算法。詳細程式如下:
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>
using namespace std;
using namespace cv;
void Sharpen(const Mat& myImage,Mat& Result);
int main( int argc, char* argv[])
{
Mat src, dst0, dst1;
src = imread("timg.jpeg");
namedWindow("Input", WINDOW_AUTOSIZE);
namedWindow("Output", WINDOW_AUTOSIZE);
namedWindow("Output1", WINDOW_AUTOSIZE);
imshow( "Input", src );
double t = (double)getTickCount();
Sharpen( src, dst0 );
t = ((double)getTickCount() - t)/getTickFrequency();
cout << "Hand written function time passed in seconds: " << t << endl;
imshow( "Output", dst0 );
Mat kernel = (Mat_<char>(3,3) << 0, -1, 0,
-1, 5, -1,
0, -1, 0);
// t = (double)getTickCount();
filter2D( src, dst1, src.depth(), kernel );
// t = ((double)getTickCount() - t)/getTickFrequency();
// cout << "Built-in filter2D time passed in seconds: " << t << endl;
imshow( "Output1", dst1 );
waitKey();
getchar();
return 0;
}
void Sharpen(const Mat& myImage,Mat& Result)
{
CV_Assert(myImage.depth() == CV_8U); // accept only uchar images
const int nChannels = myImage.channels();
Result.create(myImage.size(),myImage.type());
for(int j = 1 ; j < myImage.rows-1; ++j)
{
const uchar* previous = myImage.ptr<uchar>(j - 1);
const uchar* current = myImage.ptr<uchar>(j );
const uchar* next = myImage.ptr<uchar>(j + 1);
uchar* output = Result.ptr<uchar>(j);
for(int i= nChannels;i < nChannels*(myImage.cols-1); ++i)
{
*output++ = saturate_cast<uchar>(5*current[i]
-current[i-nChannels] - current[i+nChannels] - previous[i] - next[i]);
}
}
Result.row(0).setTo(Scalar(0));
Result.row(Result.rows-1).setTo(Scalar(0));
Result.col(0).setTo(Scalar(0));
Result.col(Result.cols-1).setTo(Scalar(0));
}
這裡包含了兩個同一個操作的兩種方法,明顯opencv自帶的更友好,方便使用。這裡我們只關注opencv自導的函式:
Mat kernel = (Mat_<char>(3,3) << 0, -1, 0,
-1, 5, -1,
0, -1, 0);
filter2D( src, dst1, src.depth(), kernel );
這簡直太簡單有木有,我們記住這個操作,並且速度明顯是經過優化的,更快。這裡我們盡情享受opencv帶給我們的便捷吧!
後記
終於寫完了這寫基礎的知識點,多少內心還是有點糾結,有些地方覺得僅僅呼叫別人寫好的東西,不過覺得了解就是生產力嘛,繼續加油。