STDIO
Tìm kiếm gần đây

    Nội dung

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

    Vũ Quang Huy

    28/09/2014
    05/04/2017
    Đọ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 chính bản thân định dạng ảnh đó. Vì vậy, ứng với mỗi định dạng ta phải có các cách đọc, ghi riêng và đôi khi phải sử dụng chính các thư viện bên ngoài chỉ để phục vụ tính năng đó (libpng, libjpeg ...). Bài viết này hướng dẫn bạn đọc sử dụng thư viện FreeImage để làm tất cả các việc này.

    Giới thiệu

    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 ta phải có các cách đọc, ghi khác nhau. Với những định dạng đơn giản như BMP hay TGA thì ta có thể dễ dàng 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ì việc 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 ta hay sử dụng thư viện của hãng thứ 3.

    Việc sử dụng thư viện của hãng thứ 3 giúp ta 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ủa chúng ta cần phải làm việc với nhiều định dạng ảnh khác nhau thì vấn đề gặp phải sẽ là sự không đồng nhất trong việc sử dụng các thư viện. Ví dụ như để load ảnh PNG với thư viện libpng thì ta 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_*.

    Yêu cầu hệ thống

    Trong bài viết này tôi sử dụng hệ điều hành Windows 8.1 Pro (x64)Visual Studio 2013 Community with Update 4.

    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 cho người sử dụng có thể 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 định dạng ảnh (37 định dạng 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.17. Trang download của FreeImage (http://freeimage.sourceforge.net/download.html) có 3 phần chính bạn đọc cần lưu ý

    ss_1 

    • Source distribution: mã nguồn của thư viện FreeImage. Ta 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 thì bạn đọc 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.

    Trong bài viết này, tôi 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 ta thu được:

    ss_2
     
    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 32bit cũng như 64bit. Folder 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 người sử dụng có thể thao tác một cách thuận tiện nhất. Chỉ cần 3 file sau ta đã 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 ta tạo một project C++ trống với tên STDIO_FreeImage_Demo.

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

    ss_14

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

    ss_4

    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 32bit. Vì vậy, tại thư mục chứa project, ta 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.

    ss_5
     
    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

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

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

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

    Do thư viện ta sử dụng là thư viện liên kết động, nên bước cuối cùng ta 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 sẽ là folder Debug. Nếu folder này chưa tồn tại, bạn đọc thử biên dịch chương trình lần đầu tiên để folder này được sinh ra.

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

    Bạn đọc quan tâm đến vấn đề thư viện liên kết tĩnh / liên kết động, cách tạo ra và sử dụng chúng thế nào có thể tham khảo bài viết Static Link Library Và Dynamic Link Library.

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

    FreeImage là một thư viện đọc ảnh khá đơn giản trong việc sử dụng. Việc đọc một 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 (bạn đọc có để dễ dàng tìm thấy ở Logo Và Nhận Dạng Stdio). Đoạn code sau mô tả việc đọc file này 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);
    }

    Trong đoạn code bên trên, dòng 9 – 14 sẽ 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 đó, ta cũng kiểm tra xem định dạng ảnh này có hợp lệ không.

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

    Dòng code 16 – 21 giúp ta 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.

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

    Dòng code 23 – 27 có tác dụng lấy thông tin của ảnh

    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);

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

    ss_10 

    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

    ss_11 

    Ví dụ minh họa

    Với hai thao tác đọc và ghi ảnh ở trên, để minh họa cho việc sử dụng. Ta có đoạn code sẽ làm các công việc như 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

    Ảnh trước chỉnh sửa Ảnh sau chỉnh sửa

    ss_12

    ss_13

    Download Demo

    STDIO_FreeImageDemo_VS2013.zip

    Tham khảo

    http://freeimage.sourceforge.net/ - 1/8/2015

    Thảo luận

    In order to comment you must be a STDIO Insider. Please sign up or log in to continue.

    Đăng nhập

    Bài viết liên quan

    Định Dạng Ảnh Bitmap - Giới Thiệu Và Các Thao Tác Cơ Bản

    Đị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, .BMP,...Với những lập trình viên đặc biệt là trong lập trình games, đồ họa,... cũng sẽ phải ...

    Phan Tấn Phúc

    13/09/2017

    Đọc Ghi File Cơ Bản Trong C#

    Đọ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ó các hàm hỗ trợ nên các thao tác đọc ghi dữ liệu trên file trong C# được thực hiện một cách nhanh ...

    Bùi Trung Hiếu

    07/04/2016

    Bản Chất Của Biến Trong C/C++

    Bản Chất Của Biến Trong C/C++

    Những ngày đầu được học và làm việc với các kiểu biến như int, float, char….Tôi luôn có những thắc mắc về:”Điều gì đang xảy ra bên trong biến int, char… khi ta cấp phát ...

    Trần Hữu Danh

    16/01/2015

    Chụp Ảnh Màn Hình Với C# Trong 5 Bước

    Chụp Ảnh Màn Hình Với C# Trong 5 Bước

    Hướng dẫn bạn đọc viết một ứng dụng bằng C# thực hiện thao tác chụp ảnh màn hình và tự động lưu lại theo định dạng chọn trước trong 5 bước. Ứng dụng có thể mở rộng thành ...

    Vũ Quang Huy

    28/09/2014

    Hướng Dẫn Sử Dụng RapidJSON Để Phân Tích JSON

    Hướng Dẫn Sử Dụng RapidJSON Để Phân Tích JSON

    Hướng dẫn lập trình với JSON bằng C++ với thư viện RapidJSON, các thao tác cơ bản như đọc, xử lý các dữ liệu trong JSON. Bài viết không giải quyết hết tất cả trường hợp ...

    Trần Thị Thu Hiền

    21/12/2014

    Toán Tử Khung Xương Trong Ảnh Nhị Phân - Skeleton Binary Morphology

    Toán Tử Khung Xương Trong Ảnh Nhị Phân - Skeleton Binary Morphology

    Toán Tử Khung Xương rút trích thành phần chính đại diện cho hình dạng của đối tượng trong ảnh nhị phân. Được ứng dụng trong nhận dạng mẫu (nhận dạng kí tự), nén ảnh (được ...

    Kim Uyên

    24/05/2016

    9 Tính Năng Quan Trọng Trong C++11

    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 gọi là C++), với những tính năng mới tối ưu hơn, dễ sử dụng hơn, dễ quản lý bộ nhớ hơn, và khắc phục ...

    Lê Minh Tài

    13/08/2015

    Xử Lý Ảnh Với OpenCV: Phóng To, Thu Nhỏ Và Xoay Ảnh

    Xử Lý Ảnh Với OpenCV: Phóng To, Thu Nhỏ Và Xoay Ảnh

    Nẳm trong loạt bài viết trong chương trình Tự Học OpenCV Qua Các Ví Dụ Thực Tế. Bài viết sẽ giới thiệu các phép biến đổi 2D, các phép biến đổi cơ sở như tịnh tiến, thay ...

    Trương Xuân Đạt

    23/01/2015

    Lưu Trữ Dữ Liệu Trong Android Với Unity

    Lưu Trữ Dữ Liệu Trong Android Với Unity

    Hướng dẫn đọc và ghi file với đường dẫn Application.dataPath, Application.streamingAssetPath, Application.persistentDataPath, Application.temporaryCachePath. Đây là 4 ...

    Bùi Trung Hiếu

    04/04/2016

    Đọc Ghi File Cơ Bản Với fstream

    Đọ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++.

    La Kiến Vinh

    04/08/2014

    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