OpenCV讀取和顯示圖片

一、從文件讀取圖像並顯示

1. 程序

基於VS2013搭建OpenCV開發環境這篇文章的最後給出了一個簡單的Demo,這個例子跟本篇使用的例子是一樣的。打開C++ IDE並創建一個新的項目,新建一個源文件,粘貼下面的代碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <opencv2\highgui\highgui.hpp>
#include <iostream>
 
using namespace cv;
using namespace std;
 
int main(int argc, const char** argv)
{
    Mat img = imread("earth.jpg", CV_LOAD_IMAGE_UNCHANGED);
    if (img.empty())
    {
        cout << "圖像加載失敗!" << endl;
        //system("pause");
        return -1;
    }
    //創建一個名字爲MyWindow的窗口
    namedWindow("MyWindow", CV_WINDOW_AUTOSIZE);
    //在MyWindow的窗中中顯示存儲在img中的圖片
    imshow("MyWindow", img);
    //等待直到有鍵按下
    waitKey(0);
    //銷燬MyWindow的窗口
    destroyWindow("MyWindow");
    return 0;
}

在運行程序之前,將圖片文件(earth.jpg)放到C++文件所在的目錄。運行程序,如下圖所示:

20140326201318

2. 解釋

下面我來解釋一下這個程序。

1
#include <opencv2\highgui\highgui.hpp>

imread(), namedWindow(), imshow() 和 waitKey() 函數都聲明在這個頭文件中,所以筆記得包含它。

上面的程序中還是用了Mat數據結構,它在”opencv2/core/core.hpp”中聲明的,那爲什麼沒有包含它呢?這是因爲在”opencv2/highgui/highgui.hpp”頭文件中已經包含了core.hpp頭文件,所以不用在我們的程序再次包含了。

1
using namespace cv;

“opencv2/core/core.hpp” 和 “opencv2/highgui/highgui.hpp中所有的數據結構和函數都聲明在cv命名空間,所以,必須在我們程序的頭部使用它,否則就要在每個OpenCV的函數和數據結構前面都要加上”cv::”(例如:cv::Mat,cv::imread()等等)。

1
Mat img = imread("earth.jpg", CV_LOAD_IMAGE_UNCHANGED);

Mat是在矩陣中存儲圖片的數據結構,它聲明在 “opencv2/core/core.hpp”頭文件中。
imread()是聲明在 “opencv2/highgui/highgui.hpp”的函數,它從文件加載一個圖片並存儲在Mat數據結構中。

imread()函數的聲明如下:

1
CV_EXPORTS_W Mat imread( const string& filename, int flags=1 );

它的參數:

    • filename —— 文件的位置。如果只提供文件名,那麼文件應該和C++文件在同一目錄,否則必須提供圖片的全路徑。
    • flags —— 有5個可能的輸入。
      • CV_LOAD_IMAGE_UNCHANGED – 在每個通道中,每個像素的位深爲8 bit,通道數(顏色)保持不變。
      • CV_LOAD_IMAGE_GRAYSCALE – 位深=8 bit 通道數=1(顏色變灰)
      • CV_LOAD_IMAGE_COLOR -位深=?, 通道數=3
      • CV_LOAD_IMAGE_ANYDEPTH – 位深不變 ,通道數=?
      • CV_LOAD_IMAGE_ANYCOLOR – 位深=?, 通道數不變

      上面的值還可以組合使用,比如:
      CV_LOAD_IMAGE_ANYDEPTH | CV_LOAD_IMAGE_ANYCOLOR – 位深不變,通道數比便
      CV_LOAD_IMAGE_COLOR | CV_LOAD_IMAGE_ANYDEPTH – 位深不變,通道數=3

如果你不確定使用哪個,就是用CV_LOAD_IMAGE_COLOR 。

要理解位深和通道的概念,應該熟悉圖像處理的理論知識,所以下面討論一點這方面的內容。

所有的數字圖像都是由像素組成的,所有的像素都有值。一個像素的最小值爲0,表示黑色。像素的值變大,它的亮度也會增強。每個像素分配的比特的固定數值是255(十進制),也就是說每個像素分配8個bit。所以一個像素的最大值爲255(二進制爲11111111)。

那麼什麼是位深呢?位深就是爲每個像素分配的比特。如果比特是8,每個像素的值可以是0-255。如果是4,每個像素的值可是0-15(二進制中爲1111)。

下面是一個8 bit位深的圖片的簡單模型。每個小矩形表示一個像素。所以每個矩形包含一個0-255的值。

GrayscaleImage

這張圖像的一些屬性:

  • 8 bit位深
  • 一個通道(所以這是一個灰度圖像)
  • 高爲4px
  • 寬爲5px
  • 分辨率爲4×5

這是一個灰度圖像(黑白圖像),因爲該圖像沒有顏色內容。像素的值越高,圖像就會越亮。像素值越低,圖像就會越暗。

下面是一個彩色圖像的簡單模型。彩色圖像至少包含3個平面:Red,Green和Blue。使用這3種顏色的特定組合可以創建任何顏色。所有的像素都是這3種顏色值的組合。(255,0,0)表示pure red。(0,255,0)表示pure green。(255,0,255)表示pure violate。它的位深爲24,因爲每個像素爲8×3 bit (每個通道8 bit)。

ColorImage

這張圖像的一些屬性:

  • 位深24 bit
  • 3個通道(所以是彩色圖像)
  • 高4px
  • 寬5px
  • 分辨率爲4×5

上面的模型,左上角的像素是(23,231,46)。它會顯示爲呈綠色的顏色,因爲green值(231)比red(23)和blue(46)都大。

1
if (img.empty())

如果imread()函數加載圖像失敗,’img’不會加載任何數據,因此,img.empty()應該返回true。檢查是否成功加載,如果沒有則退出程序是一個好的做法,否則當調用imshow()函數時,程序就會崩潰。

1
bool Mat::empty()

如果Mat::data==NULL或Mat::total()==0,這個函數返回true。

1
system("pause");

如果使用Visual Studio,這行註釋的註釋最好取消,因爲它會暫停程序,知道用戶按下任意鍵。如果不取消註釋,程序會立即退出,用戶也就不會看到錯誤信息了。

1
void namedWindow(const string& winname, int flags = WINDOW_AUTOSIZE);

這個函數創建一個窗口。它的參數如下:

  • winname——窗口的名字。這個名字會顯示在窗口的標題欄上。
  • flags——決定窗口的尺寸。有如下選項:
    • WINDOW_AUTOSIZE – 用戶不能改變圖像的尺寸,圖像顯示爲它的原有尺寸
    • CV_WINDOW_NORMAL – 調整窗口圖像的尺寸可以改變
1
void imshow(const string& winname, InputArray mat);

這個函數在指定名字的窗口中顯示存儲在mat中的圖像。如果窗口使用WINDOW_AUTOSIZE創建的,圖像會顯示爲它的原始尺寸,否則圖像會調整到窗口的尺寸大小。

它的參數:

  • winname -窗口的名字。這個名字是namedWindow()函數創建窗口時使用的
  • mat – 存儲圖像數據的Mat對象
1
int waitKey(int delay = 0)

waitKey()函數通過指定delay(毫秒)等待按鍵的時間。如果delay是0或負數,它會永久等待。如果任意鍵被按下,這個函數就會返回按下鍵的ASCII值,程序繼續執行。如果指定的時間沒有按下鍵,它返回-1,程序繼續執行。

1
void destroyWindow(const string& winname)

這個函數關閉名字爲winname的打開的窗口並釋放關聯的內存。這個函數對這個程序來說不是必須的,因爲當程序退出,操作系統通常會關閉所有打開的窗口並釋放關聯的內存。

3. 總結

當運行程序,圖像”earth.jpg”被加載到Mat類型的變量”img”。然後一個名字爲”MyWindow”的窗口打開,接着”img”被加載到窗口中。窗口和圖像一起顯示,直到按下任意鍵。

二、創建一個空圖像並顯示

這個程序和前一個非常像,唯一的不同就是這個程序創建了一個空圖像,而不是從文件中加載已存在的圖像。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <opencv2\highgui\highgui.hpp>
#include <iostream>
 
using namespace cv;
using namespace std;
 
int main(int argc, const char** argv)
{
    Mat img(500, 1000, CV_8UC3, Scalar(0, 0, 100)); //創建一個圖像 ( 3個通道, 8 bit位深, 高500, 寬1000, (0, 0, 100) 分別分配給 Blue, Green and Red. )
 
    if (img.empty())
    {
        cout << "圖像不能加載!" << endl;
        //system("pause");
        return -1;
    }
 
    namedWindow("MyWindow", CV_WINDOW_AUTOSIZE);
    imshow("MyWindow", img);
 
    waitKey(0);
 
    destroyWindow("MyWindow");
 
    return 0;
}

運行結果如下圖:

20140326235345

OpenCV的新函數

1
Mat::Mat(int rows, int cols, int type, const Scalar& s);

這是Mat的一個構造函數。它使用Scalar對象給定的值初始化Mat對象。

它的參數:

  • rows – 2維矩陣的行數 (圖像的高度像素)
  • cols – 2維矩陣的列數 ( 圖像的寬度像素)
  • type – 指定圖像的位深,數據類型和通道數。我提供 CV_8UC3 ,指定3個通道的8 bit無符號整數,下面是這個參數一些可能的輸入值:
    • CV_8UC1 – 單通道8 bit無符號整數
    • CV_8UC3 – 3通道8 bit爲無符號整數
    • CV_64FC1 – 單通道64 bit 浮點數
      如果想詳細瞭解這方面的內容,請參見OpenCV數據結構之Mat一文的“陣列的數據類型”部分。
    • s – 使用s給定的值初始化矩陣的每個元素。在上面的程序中,給定Scalar(0,0,100),因此,它使用0初始化第一個通道(Blue),0初始化第二個通道(Green),100初始化第三個通道(Red)。所以,最終的圖像是red。

總結

在這個程序中,我創建了一個高500,寬1000,有3個通道的圖像。每個通道的每個像素分配8 bit的無符號整數(每個像素 8×3=24 bit),每個像素使用(0,0,100)指定值。這意味着,第一個通道總是0,第二個通道也總是0,第三個通道總是100,因此,最終看到的是一個red的圖像。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章