opencv2 是C++接口,這就讓我有了順便複習C++的機會啊。好吧,一切重頭開始來過吧。
opencv所有的操作都是從操作圖像開始的,所以就要從圖像的組成元素之圖像像素說起啦。所以就開始介紹幾種處理像素的方法:
1,at
直接訪問圖像位置的像素 例子就是使用at直接給圖像位置(j,i)的像素賦值255,結果就是圖像上分佈了椒鹽噪聲。後面會講到哪種方法處理椒鹽噪聲效果好了。
#include <opencv2/opencv.hpp>
using namespace cv;
class class_ch2{
private:
Mat src;
Mat result;
public:
class_ch2(const Mat& img):src(img),result(img){
assert(img.size>0);
assert(src.size>0);
assert(result.size>0);
}
void salt_proc(int n){
for (int k=0;k<n;k++){
int i = rand()%src.cols;
int j = rand()%src.rows;
if (src.channels() == 1){
result.at<uchar>(j,i) = 255;
}
else{
result.at<Vec3b>(j,i)[0] = 255;
result.at<Vec3b>(j,i)[1] = 255;
result.at<Vec3b>(j,i)[2] = 255;
}
}
}
Mat get_result(){
return result;
}
};
int main(){
Mat src = imread("bridgit.jpg");
imshow("src",src);
class_ch2 cla_ch2(src);
cla_ch2.salt_proc(3000);
imshow("result",cla_ch2.get_result());
waitKey(0);
}
注意哦!!!!!!!!!!!!!!!!!
如果事先知道了圖像類型,那麼就可以使用
Mat_<uchar> src2 = src;
src2(20,20) = 255; 而不用 img.at<uchar>(j,i) = 255; 簡潔了許多,但是很多時候我們不確定我們使用的圖像在處理過程中是否會改變類型,所以就先用Mat了,如果已經確定了處理圖像的類型,那麼請不要介意,盡情的使用Mat_吧!!!
2,指針
在C版本的opencv中,貌似都是指針,因爲直接定義就是iplimage* img = 。。。。。所以大部分都是使用指針呢,所以指針並不陌生哦,只是在C++版本中多了類型控制,現在這個例子就是指針處理圖像,讓圖像顏色級數降低爲原級數的pow(2,n)分之一;
void colorReduce(const int& n){
//div_num = pow(2,n);
src.copyTo(result);
uchar mask = 0xFF<<n;
int height = result.rows;
int width = result.cols*result.channels();
for (int j= 0;j<height;j++){
uchar *pt_data = result.ptr<uchar>(j);
for (int i=0;i<width;i++){
pt_data[i] = (pt_data[i]&mask) + pow(2.0,n)/2;
}
}
}
好像水彩畫的趕腳啊。這個是n=5的效果。因爲位運算的效率很高,所以優先使用位運算。
3,isContinuous 判斷圖像是否在行尾有填補
因爲出於效率的考慮,圖像的一行經常被填補爲4或者8的倍數,利於運算,所以當圖像沒有被填補的時候,就可以被認爲是一個大小爲寬X高的一維數組。
void colorReduce(const int& n){
//div_num = pow(2,n);
src.copyTo(result);
uchar mask = 0xFF<<n;
int height = result.rows;
int width = result.cols*result.channels();
<span style="color:#3333ff;">if (result.isContinuous()){
width = height * width;
height = 1;
}</span>
for (int j= 0;j<height;j++){
uchar *pt_data = result.ptr<uchar>(j);
for (int i=0;i<width;i++){
pt_data[i] = (pt_data[i]&mask) + pow(2.0,n)/2;
}
}
}
效果跟上面一樣,就是效率的問題。對於連續圖像來說很高效。
4,迭代器
既然是C++版本的,咋麼能少了迭代器呢,模板都已經出現了的說。
void colorReduce_it(const int& n){
src.copyTo(result);
uchar mask = 0xFF<<n;
Mat_<Vec3b>::iterator it = result.begin<Vec3b>();
Mat_<Vec3b>::iterator end = result.end<Vec3b>();
for (;it!=end;it++){
(*it)[0] = ((*it)[0]&mask) + pow(2.0,n)/2;
(*it)[1] = ((*it)[1]&mask) + pow(2.0,n)/2;
(*it)[2] = ((*it)[2]&mask) + pow(2.0,n)/2;
}
}
效果呢,就是跟上面一樣,不過速度哦,就慢了。
如果之前對result是這樣定義的話:
Mat_<Vec3b> result = src;那麼兩個迭代器的定義就是這樣子的了。
<pre name="code" class="cpp">Mat_<Vec3b>::iterator it = result.begin();
Mat_<Vec3b>::iterator end = result.end();
最後對這幾個遍歷方法做個總結對比:
最具效率的一種方法:
void colorReduce_fastest(int div = 32){
src.copyTo(result);
int height = result.rows;
int width = result.cols;
if (result.isContinuous()){
width = height * width;
height = 1;
}
int n = static_cast<int>(log(static_cast<double>(div))/log(2.0));
uchar mask = 0xFF<<n;
for (int j= 0;j<height;j++){
uchar *pt_data = result.ptr<uchar>(j);
for (int i=0;i<width;i++){
*pt_data++ = (*pt_data&mask) + div/2;
*pt_data++ = (*pt_data&mask) + div/2;
*pt_data++ = (*pt_data&mask) + div/2;
}
}
}
結果跟前面一樣,但是速度是最快滴O(∩_∩)O哈哈~
像素的幾種方法遍歷方法就上面了哦。繼續加油↖(^ω^)↗