天天看點

Opencv 圖像讀取與儲存問題

本系列文章由 @yhl_leo 出品,轉載請注明出處。

文章連結: http://blog.csdn.net/yhl_leo/article/details/49737357

本文僅對 Opencv圖像讀取與儲存進行闡述,重在探讨圖像讀取與儲存過程中應注意的細節問題。

1 圖像讀取

首先看一下,imread函數的聲明:

// C++: Mat based
Mat imread(const string& filename, int flags= );

// C: IplImage based
IplImage* cvLoadImage(const char* filename, int iscolor=CV_LOAD_IMAGE_COLOR );

// C: CvMat based
CvMat* cvLoadImageM(const char* filename, int iscolor=CV_LOAD_IMAGE_COLOR );
           

此處,就不列出

python

的函數聲明。随着2.x和3.x版本不斷更新, Opencv的

C++

版本資料結構和

C

版本有較大差異,前者減少了指針的大量使用,用法更加便捷,是以建議多使用前者。以

C++

版本函數進行分析,形參清單包括:

  • filename

    : 待加載圖像(包括:檔案路徑和檔案名,圖像在工程預設路徑下的可省略檔案路徑);
  • flags

    : 标志符,指定圖像加載顔色類型,預設值為1:
    • IMREAD_UNCHANGED / CV_LOAD_IMAGE_UNCHANGED :不加改變的加載原圖。
    • IMREAD_GRAYSCALE / CV_LOAD_IMAGE_GRAYSCALE :圖像轉為灰階圖(GRAY,1通道)。
    • IMREAD_COLOR / CV_LOAD_IMAGE_COLOR :圖像轉為彩色圖(BGR,3通道)。
    • IMREAD_ANYDEPTH / CV_LOAD_IMAGE_ANYDEPTH :任何位深度,如果載入的圖像不是16-bit位圖或者32-bit位圖,則轉化為8-bit位圖。
    • IMREAD_ANYCOLOR / CV_LOAD_IMAGE_ANYCOLOR :任何彩色,單獨使用的時候等價于 IMREAD_UNCHANGED / CV_LOAD_IMAGE_UNCHANGED 。
    • > 0 :傳回3通道的彩色圖,但是如果是4通道(RGBA),其中Alpha需要保留的話,不建議這麼使用,因為一旦這麼使用,就會導緻Alpha通道被剝離掉,此時建議使用負值。
    • = 0 :傳回灰階圖像。
    • < 0 :傳回具有Alpha通道的圖像。
如果你喜歡使用

imread("file.jpg")

預設參數的形式加載圖像,務必要留意你所加載後的圖像可能已經不是你原本想要的圖像了!

從 Opencv源碼枚舉類型中也可以看到上述辨別符含義:

// highgui.hpp
enum
{
    // 8bit, color or not
    IMREAD_UNCHANGED  =-,
    // 8bit, gray
    IMREAD_GRAYSCALE  =,
    // ?, color
    IMREAD_COLOR      =,
    // any depth, ?
    IMREAD_ANYDEPTH   =,
    // ?, any color
    IMREAD_ANYCOLOR   =
};

// highui_c.h
enum
{
/* 8bit, color or not */
    CV_LOAD_IMAGE_UNCHANGED  =-,
/* 8bit, gray */
    CV_LOAD_IMAGE_GRAYSCALE  =,
/* ?, color */
    CV_LOAD_IMAGE_COLOR      =,
/* any depth, ? */
    CV_LOAD_IMAGE_ANYDEPTH   =,
/* ?, any color */
    CV_LOAD_IMAGE_ANYCOLOR   =
};
           

Opencv已經支援目前很多圖像格式,但是并非全部。主要包括:

  • Windows bitmaps    ->   

    *.bmp

    ,

    *.dib

    (always supported)
  • JPEG files    ->   

    *.jpeg

    ,

    *.jpg

    ,

    *.jpe

    (see the Notes section)
  • JPEG 2000 files    ->   

    *.jp2

    ,

    *.jpf

    ,

    *.jpx

    (see the Notes section)
  • Portable Network Graphics    ->   

    *.png

    (see the Notes section)
  • WebP    ->   

    *.webp

    (see the Notes section)
  • Portable image format    ->   

    *.pbm

    ,

    *.pgm

    ,

    *.ppm

    (always supported)
  • Sun rasters    ->   

    *.sr

    ,

    *.ras

    (always supported)
  • TIFF files    ->   

    *.tiff

    ,

    *.tif

    (see the Notes section)

    Notes

    • 1 The function determines the type of an image by the content, not by the file extension.
    • 2 On Microsoft Windows* OS and MacOSX*, the codecs shipped with an OpenCV image (libjpeg, libpng, libtiff, and libjasper) are used by default. So, OpenCV can always read JPEGs, PNGs, and TIFFs. On MacOSX, there is also an option to use native MacOSX image readers. But beware that currently these native image loaders give images with different pixel values because of the color management embedded into MacOSX.
    • 3 On Linux*, BSD flavors and other Unix-like open-source operating systems, OpenCV looks for codecs supplied with an OS image. Install the relevant packages (do not forget the development files, for example, “libjpeg-dev”, in Debian* and Ubuntu*) to get the codec support or turn on the OPENCV_BUILD_3RDPARTY_LIBS flag in CMake.
    • 4 In the case of color images, the decoded images will have the channels stored in B G R order.

對于常見的支援4通道的圖像格式來說, Opencv讀取結果是有差異的:

// 1.tif, 1.jp2 and 1.png are color images with 4 channels: R, G, B, A
cv::Mat imageTif = cv::imread("E:\\1.tif"); // the default flags is 1
cv::Mat imageJp2 = cv::imread("E:\\1.jp2"); // the default flags is 1
cv::Mat imagePng = cv::imread("E:\\1.png"); // the default flags is 1
std::cout << imageTif.channels() << std::endl; // prints 3
std::cout << imageJp2.channels() << std::endl; // prints 3
std::cout << imagePng.channels() << std::endl; // prints 3

cv::Mat imageTif2 = cv::imread("E:\\1.tif", -); // flags = -1
cv::Mat imageJp22 = cv::imread("E:\\1.jp2", -);
cv::Mat imagePng2 = cv::imread("E:\\1.png", -);
std::cout << imageTif2.channels() << std::endl; // prints 3
std::cout << imageJp22.channels() << std::endl; // prints 3
std::cout << imagePng2.channels() << std::endl; // prints 4
           

由此可見,目前 Opencv能夠直接讀取4通道圖像并保留Alpha通道的貌似隻有PNG格式,對于非PNG格式資料,需要保留Alpha通道的應用,如果堅持使用 Opencv庫,建議轉格式吧~

2 圖像存儲

首先來看,imwrite函數的聲明:

// c++: Mat based
bool imwrite(const string& filename, InputArray img, const vector<int>& params=vector<int>() );

// C: CvMat and IplImage based
int cvSaveImage(const char* filename, const CvArr* image, const int* params= );
           

仍舊以

C++

版本為例,其形參清單為:

  • filename

    :待儲存圖像名(包括:檔案路徑和檔案名,圖像在工程預設路徑下的可省略檔案路徑);
  • img

    :待儲存的圖像對象;
  • params

    :特定圖像存儲編碼參數設定,以類似

    pairs

    類型的方式,

    (paramId_1, paramValue_1)

    (paramId_2, paramValue_2)

    … ,其中

    paramId_1

    就是标志符值,

    paramValue_1

    辨別符值對應的後續參數設定:
vector<int> compression_params;
compression_params.push_back(CV_IMWRITE_PNG_COMPRESSION); // paramId_1, png compression
compression_params.push_back();                          // paramValue_2, compression level is 9 
           

在 Opencv中,主要對JPEG,PNG和PXM的編碼方式進行了特别聲明:

// highgui.hpp
enum
{
    IMWRITE_JPEG_QUALITY =,         // quality from  to , default value is  (The higher is the better)
    IMWRITE_PNG_COMPRESSION =,     // compression level from  to , default value is  (A higher value means a smaller size and longer compression time. Default value is )
    IMWRITE_PNG_STRATEGY =,
    IMWRITE_PNG_BILEVEL =,
    IMWRITE_PNG_STRATEGY_DEFAULT =,
    IMWRITE_PNG_STRATEGY_FILTERED =,
    IMWRITE_PNG_STRATEGY_HUFFMAN_ONLY =,
    IMWRITE_PNG_STRATEGY_RLE =,
    IMWRITE_PNG_STRATEGY_FIXED =,
    IMWRITE_PXM_BINARY =          // binary format flag:  or , default value is 
};

// highui_c.h
enum
{
    CV_IMWRITE_JPEG_QUALITY =,
    CV_IMWRITE_PNG_COMPRESSION =,
    CV_IMWRITE_PNG_STRATEGY =,
    CV_IMWRITE_PNG_BILEVEL =,
    CV_IMWRITE_PNG_STRATEGY_DEFAULT =,
    CV_IMWRITE_PNG_STRATEGY_FILTERED =,
    CV_IMWRITE_PNG_STRATEGY_HUFFMAN_ONLY =,
    CV_IMWRITE_PNG_STRATEGY_RLE =,
    CV_IMWRITE_PNG_STRATEGY_FIXED =,
    CV_IMWRITE_PXM_BINARY =
};
           

上述的辨別符含義,顯而易見,就不累述。值得強調的是,imwrite函數支援存儲的圖像類型是有限的隻包括:1,3,4通道的圖像,但是對于不同的圖像格式,也是有差異的:

  • 對于單通道8-bit位圖(或者16-bit位圖( CV_16U/CV_16UC1 的PNG,JPEG 2000 和TIFF))或者3通道(通道順序為:B G R )的圖像,imwrite函數是都支援的。對于格式,或者位深或者通道順序與上面不一緻的,可以使用函數

    Mat::convertTo()

    cvtColor()

    函數進行轉換後,再儲存。當然,也可以使用通用的方法利用

    FileStorage

    I/O操作,将圖像存為XML或YAML格式。
  • 對于PNG圖像,可以儲存其Alpha通道,建立一個8-bit或者16-bit 4通道的位圖(通道順序為:B G R A ),如果是全透明的Alpha通道設定為0,反之不透明設定為255/65535。

對于多通道圖像,如果想對其每個通道單獨進行儲存,當然也是可行的,一方面自己可以根據圖像的資訊和圖層資訊寫出相應的存儲函數,另一方面 Opencv也提供了專門的函數

split

可以将圖像的每個通道提取出儲存到

vector

中:

Opencv 圖像讀取與儲存問題

PNG原圖

cv::Mat img = imread( "C:\\Users\\Leo\\Desktop\\Panda.png", CV_LOAD_IMAGE_UNCHANGED );

std::vector<cv::Mat> imageChannels;
cv::split( img, imageChannels );
cv::imwrite("E:\\0.jpg", imageChannels[]);
cv::imwrite("E:\\1.jpg", imageChannels[]);
cv::imwrite("E:\\2.jpg", imageChannels[]);
cv::imwrite("E:\\3.jpg", imageChannels[]);
           

B

Opencv 圖像讀取與儲存問題

G

Opencv 圖像讀取與儲存問題

R

Opencv 圖像讀取與儲存問題

A

Opencv 圖像讀取與儲存問題

通道分離儲存結果

附上 Opencv文檔源碼:

#include <vector>
#include <stdio.h>
#include <opencv2/opencv.hpp>

using namespace cv;
using namespace std;

void createAlphaMat(Mat &mat)
{
    CV_Assert(mat.channels() == );
    for (int i = ; i < mat.rows; ++i) {
        for (int j = ; j < mat.cols; ++j) {
            Vec4b& bgra = mat.at<Vec4b>(i, j);
            bgra[] = UCHAR_MAX; // Blue
            bgra[] = saturate_cast<uchar>((float (mat.cols - j)) / ((float)mat.cols) * UCHAR_MAX); // Green
            bgra[] = saturate_cast<uchar>((float (mat.rows - i)) / ((float)mat.rows) * UCHAR_MAX); // Red
            bgra[] = saturate_cast<uchar>( * (bgra[] + bgra[])); // Alpha
        }
    }
}

int main(int argv, char **argc)
{
    // Create mat with alpha channel
    Mat mat(, , CV_8UC4);
    createAlphaMat(mat);

    vector<int> compression_params;
    compression_params.push_back(CV_IMWRITE_PNG_COMPRESSION);
    compression_params.push_back();

    try {
        imwrite("alpha.png", mat, compression_params);
    }
    catch (runtime_error& ex) {
        fprintf(stderr, "Exception converting image to PNG format: %s\n", ex.what());
        return ;
    }

    fprintf(stdout, "Saved PNG file with alpha data.\n");
    return ;
}
           

運作結果為:

Opencv 圖像讀取與儲存問題