標籤:

OpenCV之像素操作

我們首先了解一下什麼是像素,計算機中是如何存儲圖像,以及opencv是如何表示圖像的。

像素

像素是指由圖像的小方格即所謂的像素(pixel)組成的,這些小方塊都有一個明確的位置和被分配的色彩數值,而這些一小方格的顏色和位置就決定該圖像所呈現出來的樣子。可以將像素視為整個圖像中不可分割的單位或者是元素,不可分割的意思是它不能夠再切割成更小單位抑或是元素,它是以一個單一顏色的小格存在。每一個點陣圖像包含了一定量的像素,這些像素決定圖像在屏幕上所呈現的大小

計算機存儲圖像:

圖像文件存儲的都是每一個像素對應的顏色值。

1、點陣圖文件有兩種存儲像素數據的格式。16777216色(真彩色)的圖像,一個像素的顏色可以用24位數據表示。256色的圖像可以用調色板對顏色的信息進行編碼,一個像素的值對應的是調色板的索引,而不是直接對應一個像素的顏色,調色板的索引映射為像素的顏色。

2、以一百萬個像素,256種顏色的BMP文件在電腦上的存儲為例。這個文件包括一個十四位元組的文件首部,一個四十位元組的信息首部,一個1024位元組的顏色表,一兆位元組的點陣圖數據。文件首部的前兩個位元組由字元BM組成,還包括了文件長度和點陣圖數據在文件中的起始位置。

3、文件的信息首部包含了圖像的高、寬、顏色數等非圖形數據。

這個圖像共有一百萬個像素,一個像素需要八位的顏色信息,文件的這一部分的長度是一百萬個位元組,位元組排放的順序是自左到右從圖像的最下面那行開始,這個文件的總大小是1001078位元組。

opencv表示圖像:

opencv中很多數據結構為了達到內存使用的最優化,通常都會用它最小上限的空間來分配變數,有的數據結構也會因為圖像文件格式的關係而給予適當的變數,因此需要知道它們聲明的空間大小來配置適當的變數。一

般標準的圖片,為RGB格式它們的大小為8bits格式,範圍為0~255,對一個int空間的類型來說實在是太小,整整浪費了24bits的空間,假設有個640*480的BMP文件空間存儲內存,那整整浪費了640*480*3*(32-8)bits的內存空間,總共浪費了2.6MB!,也就是那

2.6MB內什麼東西都沒存儲,如果今天以8bits的格式來存儲則只使用到0.6MB的內存而已(640*480*3*(8)+54

bits),因此,對於文件格式的對應是一件很重要的事.。

訪問像素的三種方法:

  1. 指針訪問
  2. 迭代器iterator
  3. 動態地址計算

首先我們來看一段代碼:

#include<opencv2/opencv.hpp>#include<iostream>using namespace std;using namespace cv;//指針操作訪問像素void colorReduce(Mat& img,int div=64){ int nl = img.rows; int nc = img.cols*img.channels(); for(int i = 0;i < nl;i++) { uchar* data = img.ptr<uchar>(i); for(int j = 0 ; j < nc;j++) { data[j] = data[j]/div*div + div/2; } }}//迭代器模式void ColorReduce(Mat& img,int div = 64){ Mat_<Vec3b>::iterator it = img.begin<Vec3b>(); Mat_<Vec3b>::iterator itend = img.end<Vec3b>(); for (; it != itend; it++) { (*it)[0] = (*it)[0] / div * div + div / 2; (*it)[1] = (*it)[1] / div * div + div / 2; (*it)[2] = (*it)[2] / div * div + div / 2; } namedWindow("dst"); imshow("dst", img);}//動態地址計算void colorReduce2(Mat& img,int div = 64){ int cols = img.cols; int rows = img.rows; for(int i= 0;i < rows;i++) { for(int j=0; j < cols;j++) { img.at<Vec3b>(i,j)[0] = img.at<Vec3b>(i,j)[0] / div * div +div /2; img.at<Vec3b>(i,j)[1] = img.at<Vec3b>(i,j)[1] / div * div +div /2; img.at<Vec3b>(i,j)[2] = img.at<Vec3b>(i,j)[2] / div * div +div /2; } }}int main(int argc,char** argv){ Mat image = imread(argv[1]); imshow("input",image); colorReduce2(image,64); imshow("dst",image); waitKey(0); destroyAllWindows(); return 0;}

指針操作:

//指針操作訪問像素void colorReduce(Mat& img,int div=64){ int nl = img.rows; // 行數 int nc = img.cols*img.channels();//列數x通道數 = 每一行像素的個數 for(int i = 0;i < nl;i++) {     //Mat類提供了ptr函數可以得到任意行的首地址,ptr是一個模板函數 uchar* data = img.ptr<uchar>(i); //獲取第i行的首地址 for(int j = 0 ; j < nc;j++) { data[j] = data[j]/div*div + div/2; } }}

迭代器操作:

//迭代器模式void ColorReduce(Mat& img,int div = 64){ Mat_<Vec3b>::iterator it = img.begin<Vec3b>(); Mat_<Vec3b>::iterator itend = img.end<Vec3b>(); for (; it != itend; it++) { (*it)[0] = (*it)[0] / div * div + div / 2; (*it)[1] = (*it)[1] / div * div + div / 2; (*it)[2] = (*it)[2] / div * div + div / 2; } namedWindow("dst"); imshow("dst", img);}

如果不熟悉迭代器模式,可以閱讀與STL中迭代器相關的資料。

動態地址計算:

//動態地址計算void colorReduce2(Mat& img,int div = 64){ int cols = img.cols; //列數 int rows = img.rows; //行數 for(int i= 0;i < rows;i++) { for(int j=0; j < cols;j++) {  //處理B藍色通道 img.at<Vec3b>(i,j)[0] = img.at<Vec3b>(i,j)[0] / div * div +div /2;       //處理G綠色通道 img.at<Vec3b>(i,j)[1] = img.at<Vec3b>(i,j)[1] / div * div +div /2;       //處理R紅色通道 img.at<Vec3b>(i,j)[2] = img.at<Vec3b>(i,j)[2] / div * div +div /2; } }}

對於彩色圖像,每個像素由三個部分:藍色通道,綠色通道,紅色通道(BGR)。因此對於一個包含彩色圖像的Mat,會返回一個8位數組組成的向量。OpenCV將此向量定義為Vec3b,即由usigned char 組成的向量。其訪問像素通用表達式為:

image.at<Vec3b>(row,col)[channel] = value;

其中索引值表示通道。

這就是訪問像素的三種方法,我們看一下效果:


推薦閱讀:

[171103] 基於縮略圖哈希值比較的圖像相似性檢索
opencv Mat類型的轉換問題?
線性代數的直覺理解(5)
訓練隨機森林,每次結果都不同?
在visual studio+opencv中初始化圖像矩陣(從來沒見人這麼做過)?

TAG:OpenCV |