使用FreeImage幫助OpenCV讀出更多圖像(轉)
(2014-03-20 09:21:12)
轉載▼
OpenCV讀圖像的能力比較弱,尤其是對常見的 Tiff 支援得很差。通過研究 OpenCV 的源代碼,發現它針對 Tiff 隻能讀出以 LZW 和 JPEG 兩種壓縮格式儲存的單頁 RGBA 圖像,而大量使用的 G4 等二值化圖像和多頁圖像的 Tiff 檔案是不能被 OpenCV 直接讀出的。這可以使用 libTiff 幫助讀入這些 Tiff 檔案。不過使用 FreeImage 更好一些。因為在 FreeImage 中已經把 libTiff 封裝成一個 filter 了,FreeImage 提供了一個高層的圖像通路統一接口,支援了大量的圖像編解碼 filter,并且能夠找到大量基于 FreeImage 的 filter ,是以使用 FreeImage 是一個更好的選擇。
當讀入圖像的位深度小于一個位元組時,不能直接在 opencv 中建立 IplImage,可以使用 FreeImage 将這類圖像轉化為灰階圖像,然後再輸入 opencv 中。
另一個需要注意的是 FreeImage 中的圖像如果直接輸入 IplImage,則出現了水準翻轉,是以不能直接使用從 FreeImage 獲得的圖像作為 IplImage 的資料。
以下是一段把 FreeImage 的位圖轉換成 opencv 圖像的示例代碼(片段):
void zig_image_group::load_bitmap(FIBITMAP* bitmap) {
if (!bitmap) return;
// 取得位圖資料
unsigned int imageWidth = FreeImage_GetWidth(bitmap);
unsigned int imageHeight = FreeImage_GetHeight(bitmap);
unsigned short channels = 1;
unsigned short depth = FreeImage_GetBPP(bitmap);
FIBITMAP * bitmap_temp = 0;
unsigned char * data = 0;
if (depth < 8) {
// opencv 隻能建立 8 位的圖像
depth = 8;
if( NULL != (bitmap_temp = FreeImage_ConvertToGreyscale(bitmap)) ) {
data = FreeImage_GetBits(bitmap_temp);
}
} else {
assert (depth % 8 == 0);
channels = depth / 8;
depth = 8;
data = FreeImage_GetBits(bitmap);
}
// 建立 opencv 圖像,讀入位圖資訊
if(data) {
CvSize size = cvSize((int) imageWidth, (int) imageHeight);
// 如果直接從 FreeImage 得到圖像,則進行了水準翻轉,是以需要使用一個中間圖像來糾正翻轉
// 同時因為從 FreeImage 得到的圖像最終也需要複制到 IplImage 圖像中,是以隻需要一次記憶體操作
// 建立一個 IplImage 中間圖像并直接使用 FreeImage 的圖像,然後建立一個新的 IplImage 圖像,
// 并把中間圖像翻轉到新 IplImage 中,然後關閉中間圖像(隻是頭部結構)
IplImage* head = cvCreateImageHeader(size, depth, channels);
cvSetData(head, data, head->widthStep); // 注意 widthStep 是能被 4 整除的數,為了整數對齊,加速運算
object_ptr spImage(new image(size, depth, (int) channels));// 智能資料對象指針,封裝了IplImage
cvFlip(head, spImage->get_IplImage(), 0); // 翻轉并複制 FreeImage 位圖到 OpenCV 中
cvReleaseImageHeader(&head); // 釋放 head,但不釋放位圖,因為位圖是從 FreeImage 中“借”來的
m_images.push_back(adapt< object_ptr > (spImage)); // 儲存這個 IplImage 到 std::vector 中
}
if(bitmap_temp) {
FreeImage_Unload(bitmap_temp);
}
}
當圖像類型為 TIFF, ICO, GIF 時,可以使用 FreeImage 的多位圖功能讀出一組的圖像,下面是使用 FreeImage 讀取圖像的代碼片段:
void zig_image_group::load_image(const char * filename) {
// 擷取圖像類型
FREE_IMAGE_FORMAT fif = FIF_UNKNOWN;
if( FIF_UNKNOWN ==
(fif = FreeImage_GetFileType(filename, 0)) ) {
fif = FreeImage_GetFIFFromFilename(filename);
}
if( (fif == FIF_UNKNOWN) || ! FreeImage_FIFSupportsReading(fif) ) {
// 使用 free image 不能打開影像,這時再嘗試使用 opencv 打開影像
object_ptr sp_image(new image(filename, CV_LOAD_IMAGE_UNCHANGED));
if(sp_image->is_valid()) {
m_images.push_back(adapt< object_ptr >(sp_image));
} else {
cerr << "load image failure: " << filename << endl;
}
return;
}
if (fif == FIF_TIFF || fif == FIF_ICO || fif == FIF_GIF) {
// 讀一組圖像
FIMULTIBITMAP * mdib =
FreeImage_OpenMultiBitmap(fif, filename, false, true);
if(mdib) {
int pages = FreeImage_GetPageCount(mdib);
for(int page=0; page
FIBITMAP * dib = FreeImage_LockPage(mdib, page);
if(dib) {
load_bitmap(dib);
FreeImage_UnlockPage(mdib, dib, false);
}
}
FreeImage_CloseMultiBitmap(mdib);
}
} else {
// 讀一張圖像
FIBITMAP * dib = FreeImage_Load(fif, filename);
if(dib) {
load_bitmap(dib);
FreeImage_Unload(dib);
}
}
}
注意,以上代碼中使用了類似于 ATL 的兩個智能指針模闆:object_ptr (類似于 atl::CComPtr), adapt (類似于 atl::CAdapt)。有了這兩個智能指針模闆,就可以友善地使用 STL 容器存放各種資料對象了。
http://hi.baidu.com/zighouse/item/b16d906d3a96c90ea1cf0fdf