天天看點

使用FreeImage幫助OpenCV讀出更多圖像(轉)

使用FreeImage幫助OpenCV讀出更多圖像(轉)

(2014-03-20 09:21:12)

使用FreeImage幫助OpenCV讀出更多圖像(轉)

轉載▼

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