STDIO
Tìm kiếm gần đây
    • Nội dung
    • QR Code
    • 0
    • 0
    • Sao chép

    Đọc Ghi Tất Cả Các Định Dạng Ảnh với FreeImage trong C++

    Hướng dẫn sử dụng FreeImage để đọc, ghi, xử lý ảnh trong C++.

    Vũ Quang Huy

    28/09/2014
    06/09/2020
    9 phút đọc
    Đọc Ghi Tất Cả Các Định Dạng Ảnh với FreeImage trong C++

    Mỗi định dạng ảnh (*.jpg, *.png, *.tga, *.bmp ...) đều có những quy chuẩn về lưu trữ của riêng chúng. Tùy theo mỗi định dạng mà có các cách đọc, ghi khác nhau. Với những định dạng đơn giản như BMP hay TGA có thể thao tác thông qua mô tả về cách lưu. Tuy nhiên, đối với một số định dạng phức tạp hơn thì cài đặt lại các thao tác tốn một thời gian không nhỏ. Do đó với những định dạng như thế - ví dụ như PNG hay JPG / JPEG có thể sử dụng thư viện của bên thứ 3.

    Việc sử dụng thư viện của bên thứ 3 giúp giải quyết được bài toán thao tác với các thư viện ảnh khác nhau, nhưng nếu ứng dụng cần làm việc với nhiều định dạng ảnh khác nhau thì vấn đề gặp phải là sự không đồng nhất trong việc sử dụng các thư viện. Ví dụ để load ảnh PNG với thư viện libpng sẽ có các hàm bắt đầu với png_*, trong khi load ảnh JPG với libjpeg thì cần phải dùng các hàm bắt đầu với jpeg_*.

    Thư viện FreeImage

    FreeImage là một thư viện được cung cấp miễn phí, chức năng chính của thư viện giúp thao tác với các định dạng ảnh phổ biến cũng như không được phổ biến. Theo như tài liệu của nhà phát triển, FreeImage hỗ trợ các 37+ định dạng ảnh khác nhau:

    • Windows or OS/2 Bitmap File (*.BMP)
    • Dr. Halo (*.CUT)
    • DirectDraw Surface (*.DDS)
    • ILM OpenEXR (*.EXR)
    • Raw Fax format CCITT G3 (*.G3)
    • Graphics Interchange Format (*.GIF)
    • High Dynamic Range (*.HDR)
    • Windows Icon (*.ICO)
    • Amiga IFF (*.IFF, *.LBM)
    • JPEG-2000 codestream (*.J2K, *.J2C)
    • JPEG Network Graphics (*.JNG)
    • JPEG-2000 File Format (*.JP2)
    • Independent JPEG Group (*.JPG, *.JIF, *.JPEG, *.JPE)
    • JPEG XR image format (*.JXR, *.WDP, *.HDP)
    • Commodore 64 Koala format (*.KOA)
    • Multiple Network Graphics (*.MNG)
    • Portable Bitmap (ASCII) (*.PBM)
    • Portable Bitmap (BINARY) (*.PBM)
    • Kodak PhotoCD (*.PCD)
    • Zsoft Paintbrush PCX bitmap format (*.PCX)
    • Portable Floatmap (*.PFM)
    • Portable Graymap (ASCII) (*.PGM)
    • Portable Graymap (BINARY) (*.PGM)
    • Macintosh PICT (*.PCT, *.PICT, *.PIC)
    • Portable Network Graphics (*.PNG)
    • Portable Pixelmap (ASCII) (*.PPM)
    • Portable Pixelmap (BINARY) (*.PPM)
    • Adobe Photoshop (*.PSD)
    • Sun Rasterfile (*.RAS)
    • RAW camera image (many extensions)
    • Silicon Graphics SGI image format (*.SGI)
    • Truevision Targa files (*.TGA, *.TARGA)
    • Tagged Image File Format (*.TIF, *.TIFF)
    • Wireless Bitmap (*.WBMP)
    • Google WebP image format (*.WEBP)
    • X11 Bitmap Format (*.XBM)
    • X11 Pixmap Format (*.XPM)

    Phiên bản hiện tại của FreeImage là 3.18. Trang download của FreeImage (http://freeimage.sourceforge.net/download.html) có 3 phần chính cần lưu ý:

    • Source distribution: mã nguồn của thư viện FreeImage, cần phải biên dịch thành định dạng thư viện mong muốn trước khi sử dụng, như *.lib đối với Windows hay *.a đối với Linux.
    • FreeImage DLL: mã nguồn đã được biên dịch thành các file thư viện. Nếu ứng dụng không yêu cầu phải cross-platform nên sử dụng tùy chọn này để giảm thiểu sự phức tạp cũng như độ lớn cho chương trình. 
    • FreeImage Documentation: tài liệu kỹ thuật liên quan đến việc sử dụng thư viện FreeImage.

    Bài viết này sử dụng thư viện FreeImage đã được biên dịch ra sẵn các file DLL. Tập tin khi tải về sẽ có dạng FreeImage3170Win32Win64.zip. Giải nén tập tin này:

    Sử dụng thư viện Freeimage để load ảnh với C++.

    Với các folder đã giải nén, folder quan trọng nhất là:

    • Dist: chứa thư viện FreeImage được compile dành do máy tính 32-bit cũng như 64-bit.
    • Example: chứa ví dụ mẫu hướng dẫn cách sử dụng thư viện.
    • Wrapper: chứa thư viện FreeImage đã được port qua cho các ngôn ngữ khác như .NET, VB.

    Sử dụng thư viện FreeImage

    Thư viện FreeImage đã được tổ chức sao cho có thể thao tác một cách thuận tiện nhất. Chỉ cần 3 file sau đã có thể sử dụng được thư viện

    • FreeImage.h
    • FreeImage.lib
    • FreeImage.dll

    Để minh họa việc sử dụng thư viện này, bước đầu tiên tạo một project C++ trống với tên STDIO_FreeImage_Demo.

    Tạo project để sử dụng FreeImage trong C++.

    Sau đó tạo thêm file Source.cpp và đưa vào solution:

    Tạo project sử dụng FreeImage.

    Kiểm tra thư mục chứa project sau khi tạo sẽ có:

    Thêm thư viện FreeImage vào project

    Project khi được tạo ra mặc định của Visual Studio sẽ được compile cho hệ thống 32-bit. Vì vậy, tại thư mục chứa project, tạo thêm 1 folder tên Lib - chứa thư viện FreeImage - và copy toàn bộ nội dung trong folder <extracted_path>\Dist\x32 đã giải nén ở trên vào folder Lib vừa tạo.

    Thư viện FreeImage trong C++.

    Và ta tiến hành add 3 file trên vào project qua option Project Properties > Configuration Properties. Tại C/C++ > General > Additional Includes Directory, thêm vào đường dẫn $(SolutionDir)\Lib

    Thêm thư viện FreeImage vào project Visual Studio.

    Tại Linker > General > Additional Library Directories, ta cũng thêm vào đường dẫn $(SolutionDir)\Lib

    Vẫn tại Linker, chọn mục Input > Additional Dependencies và thêm vào FreeImage.lib

    Thêm thư viện liên kết FreeImage vào Visual Studio C++.

    Chọn OK để chấp nhận các chỉnh sửa.

    Do thư viện được sử dụng là thư viện liên kết động, nên bước cuối cùng cần copy file FreeImage.dll vào thư mục chứa file thực thi của chương trình. Trong trường hợp này là folder Debug. Nếu folder này chưa tồn tại, thử biên dịch chương trình lần đầu tiên để folder này được sinh ra.

    FreeImage.dll vào project.

    Đến đây đã kết thúc quá trình thêm thư viện FreeImage vào project. Tiếp theo tiến hành các bước để đọc và ghi file ảnh.

    Đọc ảnh với thư viện FreeImage

    FreeImage là 1 thư viện đọc ảnh khá đơn giản trong việc sử dụng. Đọc 1 file ảnh từ các thiết bị lưu trữ với FreeImage có thể được gói gọn trong những bước sau:

    • Xác định định dạng ảnh và load lên thông qua hàm FreeImage_GetFileTypeFreeImage_Load.
    • Lấy các thông tin liên quan bằng tổ hợp các hàm FreeImage_GetWidth, FreeImage_GetHeight, ...
    • Lấy dữ liệu ảnh thông qua hàm FreeImage_GetBits.
    • Xử lý ảnh.
    • Lưu lại ảnh (sẽ được đề cập bên dưới).
    • Giải phóng vùng nhớ sau khi sử dụng với hàm FreeImage_Unload.

    Giả sử ta có file stdio-logo.png (tải tại đây). Đoạn code sau đọc file ảnh và hiển thị các thông tin liên quan.

    #include <stdio.h>
    #include "FreeImage.h"
    
    int main()
    {
    	char *img_path = "stdio-logo.png";
    	int img_width = 0, img_height = 0, img_bpp = 0;
    	
    	FREE_IMAGE_FORMAT img_format = FreeImage_GetFileType(img_path, 0);
    	if (img_format == FREE_IMAGE_FORMAT::FIF_UNKNOWN)
    	{
    		printf("Error: Unknown format !");
    		return 1;
    	}
    
    	FIBITMAP* img_bm = FreeImage_Load(img_format, img_path);
    	if (img_bm == NULL)
    	{
    		printf("Error: Image load FAIL !");
    		return 1;
    	}		
    
    	img_width = FreeImage_GetWidth(img_bm);
    	img_height = FreeImage_GetHeight(img_bm);
    	img_bpp = FreeImage_GetBPP(img_bm);
    
    	char *pixels = (char*)FreeImage_GetBits(img_bm);
    
    	// Print image's info
    	printf("Image path: %s\n", img_path);
    	printf("Image width: %d\n", img_width);
    	printf("Image height: %d\n", img_height);
    	printf("Image BPP (bit-per-pixel): %d", img_bpp);
    
    	// Clean up
    	FreeImage_Unload(img_bm);
    }

    Đoạn code...

    FREE_IMAGE_FORMAT img_format = FreeImage_GetFileType(img_path, 0);
    if (img_format == FREE_IMAGE_FORMAT::FIF_UNKNOWN)
    {
        printf("Error: Unknown format !");
        return 1;
    }

    ...lấy định dạng của file ảnh qua hàm FreeImage_GetFileType. Hàm này nhận vào đường dẫn của file ảnh và trả về định dạng của nó. Song song đó, kiểm tra xem định dạng ảnh này có hợp lệ không.

    Đoạn code...

    FIBITMAP* img_bm = FreeImage_Load(img_format, img_path);
    if (img_bm == NULL)
    {
        printf("Error: Image load FAIL !");
        return 1;
    }

    ...load ảnh vào bộ nhớ dùng hàm FreeImage_Load với định dạng ảnh cho trước. FIBITMAP là kiểu dữ liệu được xây dựng sẵn của FreeImage, chứa toàn bộ thông tin của ảnh được load.

    Đoạn code...

    img_width = FreeImage_GetWidth(img_bm);
    img_height = FreeImage_GetHeight(img_bm);
    img_bpp = FreeImage_GetBPP(img_bm);
    
    char *pixels = (char*)FreeImage_GetBits(img_bm);

    ... có tác dụng lấy thông tin của ảnh.

    Biên dịch và chạy thử, nếu không có lỗi xảy ra, thu được kết quả:

    Đọc thông tin ảnh trong C++ với FreeImage.

    Tạo / Lưu ảnh với FreeImage

    Với FreeImage, file ảnh được tạo theo các bước sau:

    • Cấp phát vùng nhớ cho bức ảnh dùng hàm FreeImage_Allocate, thông tin bao gồm chiều dài, chiều rộng và số lượng bit trên mỗi pixel. Hàm này trả về đối tượng FIBITMAP
    • Ghi dữ liệu vào ảnh
    • Lưu lại file ảnh sử dụng hàm FreeImage_Save với thông tin về định dạng ảnh, đường dẫn, ...

    Đoạn code sau minh họa cho việc tạo một bức ảnh có màu đồng nhất.

    #include <stdio.h>
    #include "FreeImage.h"
    
    int main()
    {
        char *img_path = "stdio-image-demo.png";
        int img_width = 32, img_height = 32, img_bpp = 32;
    
        FIBITMAP* img_bm = FreeImage_Allocate(img_width, img_height, img_bpp);
        int* pixels = (int*)FreeImage_GetBits(img_bm);
    
        int size = img_width * img_height;
        for (int i = 0; i < size; i++)
            pixels[i] = 0xffffbb33;
        
        if (FreeImage_Save(FREE_IMAGE_FORMAT::FIF_PNG, img_bm, img_path))
        {
            printf("Info: Save success !!");
        }
        else
        {
            printf("Info: Save fail !!");
        }
    
        // Clean up
        FreeImage_Unload(img_bm);
    }

    Bức ảnh được sinh ra có kích thước 32x32 pixel và mã màu là 0xffbb33

    Ví dụ minh họa

    Với hai thao tác đọc và ghi ảnh ở trên, để minh họa cho cách dùng, có thẻ sử dụng đoạn code sau:

    • Đọc file ảnh stdio-logo.png
    • Đổi màu thành trắng đen
    • Lưu lại thành stdio-logo-gray.png
    #include <stdio.h>
    #include "FreeImage.h"
    
    int main()
    {
        char *img_path = "stdio-logo.png";
        char *img_out_path = "stdio-logo-gray.png";
        int img_width = 0, img_height = 0, img_bpp = 0;
    
        // Load original image
        FREE_IMAGE_FORMAT img_format = FreeImage_GetFileType(img_path, 0);
        if (img_format == FREE_IMAGE_FORMAT::FIF_UNKNOWN)
        {
            printf("Error: Unknown format !");
            return 1;
        }
    
        FIBITMAP* img_bm = FreeImage_Load(img_format, img_path);
        if (img_bm == NULL)
        {
            printf("Error: Image load FAIL !");
            return 1;
        }
    
        // Get neccessary infomation
        img_width = FreeImage_GetWidth(img_bm);
        img_height = FreeImage_GetHeight(img_bm);
        img_bpp = FreeImage_GetBPP(img_bm);
    
        // Modify image content
        unsigned int *pixels = (unsigned int*)FreeImage_GetBits(img_bm);
        int img_size = img_width * img_height;
    
        for (int i = 0; i < img_size; i++)
        {
            unsigned char gray_level = (((pixels[i] & 0x00ff0000) >> 16) + 
                                            ((pixels[i] & 0x0000ff00) >> 8) + 
                                            (pixels[i] & 0x000000ff)) / 3;
            unsigned char alpha_level = (pixels[i] & 0xff000000) >> 24;
            pixels[i] = (alpha_level << 24) | (gray_level << 16) | (gray_level << 8) | gray_level;
        }
    
        // Save modified image
        if (FreeImage_Save(FREE_IMAGE_FORMAT::FIF_PNG, img_bm, img_out_path))
        {
            printf("Info: Save success !!");
        }
        else
        {
            printf("Info: Save fail !!");
        }
    
        // Clean up
        FreeImage_Unload(img_bm);
    }

    Và đây là kết quả thu được:

    stdio-logo.png stdio-logo-gray.png
    Logo STDIO STDIO Logo xám

    Download Demo

    STDIO_FreeImage2019.zip

    Tham khảo

    • https://freeimage.sourceforge.io/ - 6/9/2020
    0
    Khi bạn nhấn vào liên kết sản phẩm do STDIO đề xuất và mua hàng, STDIO có thể nhận được hoa hồng. Điều này hỗ trợ STDIO tạo thêm nhiều nội dung hữu ích.. Tìm hiểu thêm.

    Đề xuất

    Định Dạng Ảnh Bitmap - Giới Thiệu và Các Thao Tác Cơ Bản
    Trong đời thường, bạn sẽ gặp các file ảnh có định dạng .PNG, .JPG, .TGA, ...
    Đọc Ghi File Cơ Bản Trong C#
    Hướng dẫn đọc và ghi file cơ bản sử dụng đối tượng File trong C#. Nhờ có ...

    Khám phá

    9 Tính Năng Quan Trọng Trong C++11
    C++11 là một phiên bản cải tiến và nâng cấp từ C++98 (hay các bạn vẫn ...
    13/08/2015
    Hướng Dẫn Sử Dụng RapidJSON để Xử Lý JSON trong C++
    Khái niệm về định dạng JSON và cách sử dụng RapidJSON để thao tác với ...
    Đọc Ghi File Cơ Bản với fstream
    Sử dụng fstream để đọc file, ghi file, xử lý file đơn giản trong C++.
    04/08/2014
    Phép Tích Chập Trong Xử Lý Ảnh (Convolution)
    Convolution là kỹ thuật quan trọng trong Xử Lý Ảnh, được sử dụng chính ...
    Nhập Xuất Cơ Bản trong C/C++
    Bài viết là tiền đề giúp cho người đọc làm quen các thao tác cơ bản với ...
    18/03/2016
    Chụp Ảnh Màn Hình Với C# Trong 5 Bước
    Hướng dẫn xây dựng ứng dụng bằng C# thực hiện thao tác chụp ảnh màn hình ...
    Cấu Trúc File CSV và Cách Đọc - Ghi File CSV
    File .csv là gì, cấu trúc file CSV - cách tạo, đọc ghi file csv với các ...
    Giới Thiệu Về Sprite Trong Cocos2d-x 3.x.x
    Giới thiệu tổng quan về khái niệm cơ bản, một số cách dùng để khởi tạo ...
    STDIO
    Trang chính
    Công ty TNHH STDIO

    30, Trịnh Đình Thảo, Hòa Thạnh, Tân Phú, Hồ Chí Minh
    +84 28.36205514 - +84 942.111912
    developer@stdio.vn

    383/1 Quang Trung, Phường 10, Quận Gò Vấp, Hồ Chí Minh
    Số giấy phép ĐKKD: 0311563559 do sở Kế hoạch và Đầu Tư TPHCM cấp ngày 23/02/2012

    ©STDIO, 2013 - 2020