【從零學習OpenCV】影像的保存&影片的保存

  • 2019 年 11 月 12 日
  • 筆記

經過幾個月的努力,小白終於完成了市面上第一本OpenCV 4入門書籍《從零學習OpenCV 4》。為了更讓小夥伴更早的了解最新版的OpenCV 4,小白與出版社溝通,提前在公眾號上連載部分內容,請持續關注小白。

01、影像的保存

OpenCV提供imwrite()函數用於將Mat類矩陣保存成影像文件,該函數的函數原型在程式碼清單2-30中給出。

程式碼清單2-30 imwrite()函數原型  bool cv :: imwrite(const String& filename,                        InputArray img,                        Const std::vector<int>& params = std::vector<int>()                        )
  • filename:保存影像的地址和文件名,包含影像格式
  • img:將要保存的Mat類矩陣變數
  • params:保存圖片格式屬性設置標誌

該函數用於將Mat類矩陣保存成影像文件,如果成功保存,則返回true,否則返回false。可以保存的影像格式參考imread()函數能夠讀取的影像文件格式,通常使用該函數只能保存8位單通道影像和3通道BGR彩色影像,但是可以通過更改第三個參數保存成不同格式的影像。不同影像格式能夠保存的影像位數如下:

  • 16位無符號(CV_16U)影像可以保存成PNG、JPEG、TIFF格式文件;
  • 32位浮點(CV_32F)影像可以保存成PFM、TIFF、OpenEXR和Radiance HDR格式文件;
  • 4通道(Alpha通道)影像可以保存成PNG格式文件。

函數第三個參數在一般情況下不需要填寫,保存成指定的文件格式只需要直接在第一個參數後面更改文件後綴即可,但是當需要保存的Mat類矩陣中數據比較特殊時(如16位深度數據),則需要設置第三個參數。第三個參數的設置方式如程式碼清單2-31中所示,常見的可選擇設置標誌在表2-6中給出。

程式碼清單2-31 imwrite()函數中第三個參數設置方式  vector <int> compression_params;  compression_params.push_back(IMWRITE_PNG_COMPRESSION);  compression_params.push_back(9);  imwrite(filename, img, compression_params)

表2-6 imwrite()函數第三個參數可選擇的標誌及作用

標誌參數

簡記

作用

IMWRITE_JPEG_QUALITY

1

保存成JPEG格式的文件的影像品質,分成0-100等級,默認95

IMWRITE_JPEG_PROGRESSIVE

2

增強JPEG格式,啟用為1,默認值為0(False)

IMWRITE_JPEG_OPTIMIZE

3

對JPEG格式進行優化,啟用為1,默認參數為0(False)

IMWRITE_JPEG_LUMA_QUALITY

5

JPEG格式文件單獨的亮度品質等級,分成0-100,默認為0

IMWRITE_JPEG_CHROMA_QUALITY

6

JPEG格式文件單獨的色度品質等級,分成0-100,默認為0

IMWRITE_PNG_COMPRESSION

16

保存成PNG格式文件壓縮級別,從0-9,只越高意味著更小尺寸和更長的壓縮時間,默認值為1(最佳速度設置)

IMWRITE_TIFF_COMPRESSION

259

保存成TIFF格式文件壓縮方案

為了更好的理解imwrite()函數的使用方式,在程式碼清單2-32中給出了生成帶有Alpha通道的矩陣,並保存成PNG格式影像的程式。程式運行後會生成一個保存了4通道的png格式影像,為了更直觀的看到影像結果,我們在圖2-8中給出了Image Watch插件中看到的影像和保存成png格式的影像。

程式碼清單2-32 imgWriter.cpp保存影像  1.  #include <opencv2opencv.hpp>  2.  #include <iostream>  3.  4.  using  namespace std;  5.  using  namespace cv;  6.  7.  void AlphaMat(Mat &mat)  8. {  9.     CV_Assert(mat.channels() == 4);  10.    for (int i = 0; i < mat.rows; ++i)  11.    {  12.      for (int j = 0; j < mat.cols; ++j)  13.      {  14.        Vec4b& bgra = mat.at<Vec4b>(i, j);  15.        bgra[0] = UCHAR_MAX; // 藍色通道  16.        bgra[1] = saturate_cast<uchar>((float(mat.cols - j)) / ((float)mat.cols)  17.                                                                          * UCHAR_MAX); // 綠色通道  18.        bgra[2] = saturate_cast<uchar>((float(mat.rows - i)) / ((float)mat.rows)  19.                                                                           * UCHAR_MAX); // 紅色通道  20.        bgra[3] = saturate_cast<uchar>(0.5 * (bgra[1] + bgra[2])); // Alpha通道  21.      }  22.    }  23.  }  24.  int main(int agrc, char** agrv)  25. {  26.    // Create mat with alpha channel  27.    Mat mat(480, 640, CV_8UC4);  28.    AlphaMat(mat);  29.    vector<int> compression_params;  30.    compression_params.push_back(IMWRITE_PNG_COMPRESSION); //PNG格式影像壓縮標誌  31.    compression_params.push_back(9); //設置最高壓縮品質  32.    bool result = imwrite("alpha.png", mat, compression_params);  33.    if (!result)  34.    {  35.      cout<< "保存成PNG格式影像失敗" << endl;  36.      return -1;  37.    }  38.      cout << "保存成功" << endl;  39.    return 0;  40.  }

圖2-8 程式中和保存後的四通道影像(左:Image Watc, 右::png文件)

影片的保存

有時我們需要將多幅影像生成影片,或者直接將攝影機拍攝到的數據保存成影片文件。OpenCV中提供了VideoWrite()類用於實現多張影像保存成影片文件,該類構造函數的原型在程式碼清單2-33中給出。

程式碼清單2-33 讀取影片文件VideoCapture類構造函數  cv :: VideoWriter :: VideoWriter(); //默認構造函數  cv :: VideoWriter :: VideoWriter(const String& filename,                                         int fourcc,                                         double  fps,                                         Size frameSize,                                         bool  isColor=true                                         )
  • filename:保存影片的地址和文件名,包含影片格式
  • int:壓縮幀的4字元編解碼器程式碼,詳細參數在表2-7給出。
  • fps:保存影片的幀率,即影片中每秒影像的張數。
  • framSize:影片幀的尺寸
  • isColor:保存影片是否為彩色影片

程式碼清單2-33中的第1行默認構造函數的使用方法與VideoCapture()相同,都是創建一個用於保存影片的數據流,後續通過open()函數設置保存文件名稱、編解碼器、幀數等一系列參數。第二種構造函數需要輸入的第一個參數是需要保存的影片文件名稱,第二個函數是編解碼器的程式碼,可以設置的編解碼器選項在表中給出,如果賦值「-1」則會自動搜索合適的編解碼器,需要注意的是其在OpenCV 4.0版本和OpenCV 4.1版本中的輸入方式有一些差別,具體差別在表2-7給出。第三個參數為保存影片的幀率,可以根據需求自由設置,例如實現原影片二倍速播放、原影片慢動作播放等。第四個參數是設置保存的影片文件的尺寸,這裡需要注意的時,在設置時一定要與影像的尺寸相同,不然無法保存影片。最後一個參數是設置保存的影片是否是彩色的,程式中,默認的是保存為彩色影片。

該函數與VideoCapture()有很大的相似之處,都可以通過isOpened()函數判斷是否成功創建一個影片流,可以通過get()查看影片流中的各種屬性。在保存影片時,我們只需要將生成影片的影像一幀一幀通過「<<」操作符(或者write()函數)賦值給影片流即可,最後使用release()關閉影片流。

表2-7 影片編碼格式

OpenCV 4.1版本標誌

OpenCV 4.0版本標誌

作用

VideoWriter::fourcc('D','I','V','X')

CV_FOURCC('D','I','V','X')

MPEG-4編碼

VideoWriter::fourcc('P','I','M','1')

CV_FOURCC('P','I','M','1')

MPEG-1編碼

VideoWriter::fourcc('M','J','P','G')

CV_FOURCC('M','J','P','G')

JPEG編碼(運行效果一般)

VideoWriter::fourcc('M', 'P', '4', '2')

CV_FOURCC('M', 'P', '4', '2')

MPEG-4.2編碼

VideoWriter::fourcc('D', 'I', 'V', '3')

CV_FOURCC('D', 'I', 'V', '3')

MPEG-4.3編碼

VideoWriter::fourcc('U', '2', '6', '3')

CV_FOURCC('U', '2', '6', '3')

H263編碼

VideoWriter::fourcc('I', '2', '6', '3')

CV_FOURCC('I', '2', '6', '3')

H263I編碼

VideoWriter::fourcc('F', 'L', 'V', '1')

CV_FOURCC('F', 'L', 'V', '1')

FLV1編碼

為了更好的理解VideoWrite()類的使用方式,程式碼清單2-34中給出了利用已有影片文件數據或者直接通過攝影機生成新的影片文件的常式。讀者需要重點體會VideoWrite()類和VideoCapture()類的相似之處和使用時的注意事項。

程式碼清單2-34 VideoWriter.cpp保存影片文件  1.  #include <opencv2opencv.hpp>  2.  #include <iostream>  3.  4.  using namespace cv;  5.  using namespace std;  6.  7.  int main()  8. {  9.     Mat img;  10.    VideoCapture video(0); //使用某個攝影機  11.  12.    //讀取影片  13.    //VideoCapture video;  14.    //video.open("cup.mp4");  15.  16.    if (!video.isOpened()) // 判斷是否調用成功  17.    {  18.      cout << "打開攝影機失敗,請確實攝影機是否安裝成功";  19.      return -1;  20.    }  21.  22.    video >> img; //獲取影像  23.    //檢測是否成功獲取影像  24.    if (img.empty()) //判斷有沒有讀取影像成功  25.    {  26.      cout << "沒有獲取到影像" << endl;  27.      return -1;  28.    }  29.    bool isColor = (img.type() == CV_8UC3); //判斷相機(影片)類型是否為彩色  30.  31.    VideoWriter writer;  32.    int codec = VideoWriter::fourcc('M', 'J', 'P', 'G'); // 選擇編碼格式  33.    //OpenCV 4.0版本設置編碼格式  34.    //int codec = CV_FOURCC('M', 'J', 'P', 'G');  35.  36.    double fps = 25.0; //設置影片幀率  37.    string filename = "live.avi"; //保存的影片文件名稱  38.    writer.open(filename, codec, fps, img.size(), isColor); //創建保存影片文件的影片流  39.  40.    if (!writer.isOpened()) //判斷影片流是否創建成功  41.    {  42.      cout << "打開影片文件失敗,請確實是否為合法輸入" << endl;  43.      return -1;  44.    }  45.  46.    while (1)  47.    {  48.      //檢測是否執行完畢  49.      if (!video.read(img)) //判斷能都繼續從攝影機或者影片文件中讀出一幀影像  50.      {  51.        cout << "攝影機斷開連接或者影片讀取完成" << endl;  52.        break;  53.      }  54.      writer.write(img); //把影像寫入影片流  55.      //writer << img;  56.      imshow("Live", img); //顯示影像  57.      char c = waitKey(50);  58.      if (c == 27) //按ESC案件退出影片保存  59.      {  60.        break;  61.      }  62.    }  63.    // 退出程式時刻自動關閉影片流  64.    //video.release();  65.    //writer.release();  66.    return 0;  67.  }