Decomprimere un file immagine jpeg per ottenerne i dati grezzi mediante le funzioni esterne del API di libjpeg
Il formato JPEG (Joint Photographic Experts Group) è uno standard di compressione delle immagini, arrivando a ridurre le dimensioni di un file immagine anche sino al 5%.
E' possibile ottenere da un file immagine JPEG i suoi dati in formato grezzo utilizzando alcune funzioni della libreria esterna (nella sua attuale versione): libjpeg.so.8.0.2.
Mostriamo di seguito un esempio di decodifica di un file JPEG, nel quale utilizzeremo - al fine di gestire con sicurezza una parte di codice - istruzioni in linguaggio C che saranno poste in un'apposita libreria esterna da noi creata.
Tale liberia ad hoc sarà così scritta:
#include <stdio.h> #include <stdlib.h> #include <jpeglib.h> unsigned char * bmp_buffer; unsigned char * Legge_linee(struct jpeg_decompress_struct * cinfo, int bmp_size, int row_stride) { bmp_buffer = (unsigned char*) malloc(bmp_size); while (cinfo->output_scanline < cinfo->output_height) { unsigned char *buffer_array[1]; buffer_array[0] = bmp_buffer + (cinfo->output_scanline) * row_stride; jpeg_read_scanlines(cinfo, buffer_array, 1); } return bmp_buffer; }
Nel nostro esempio tale sorgente della libreria esterna verrà salvato con il nome libadhoc.c nella cartella Dati del progamma principale scritto in Gambas.
Il progamma principale scritto in Gambas, che mostrerà anche alcune informazioni di carattere generale sul file JPEG caricato, potrà avere il seguente tenore:
Library "libjpeg:8.0.2" ' struct jpeg_error_mgr * jpeg_std_error(struct jpeg_error_mgr * error_mgr) ' Update the given jpeg_error_mgr object with standard error handling methods. Private Extern jpeg_std_error(error_mgr As Pointer) As Pointer ' void jpeg_CreateDecompress(j_decompress_ptr cinfo, int version, size_t size) ' Allocate and initialize a JPEG decompression object of type struct jpeg_decompress_struct. Private Extern jpeg_CreateDecompress(cinfo_ptr As Pointer, version As Integer, size As Integer) ' void jpeg_mem_src(j_decompress_ptr cinfo, unsigned char * inbuffer, unsigned long insize) ' Data source and destination managers: memory buffers. Private Extern jpeg_mem_src(cinfo_ptr As Pointer, inbuffer As Byte[], insize As Long) ' int jpeg_read_header(j_decompress_ptr cinfo, boolean require_image) ' Read the JPEG datastream until the first SOS marker is encountered. Private Extern jpeg_read_header(cinfo_ptr As Pointer, require_image As Boolean) As Integer ' boolean jpeg_start_decompress(j_decompress_ptr cinfo) ' Initialize state for a JPEG decompression cycle and allocate working memory. Private Extern jpeg_start_decompress(cinfo_ptr As Pointer) As Boolean ' boolean jpeg_finish_decompress(j_decompress_ptr cinfo) ' Set the decompression state to completion. Private Extern jpeg_finish_decompress(cinfo_ptr As Pointer) As Boolean ' void jpeg_destroy_decompress(j_decompress_ptr cinfo) ' Deallocate and release all memory associated with the decompression object. Private Extern jpeg_destroy_decompress(cinfo_ptr As Pointer) Private Extern Legge_linee(cinfo_ptr As Pointer, bmp_buf As Pointer, row As Integer) As Pointer In "/tmp/libadhoc" Public Sub Main() Dim percorso, header As String Dim mgr, cinfo, bmp As Pointer Dim st As Stream Dim dim_jpg, dim_bmp As Long Dim fl As File Dim prog_riga, width, height, dim_pixel, rc As Integer Dim buffer, buf, bb As Byte[] ' Creiamo la libreria esterna condivisa .so da noi scritta: Shell "gcc -o /tmp/libadhoc.so " & Application.Path &/ "libadhoc.c -shared -fPIC -ljpeg" Wait percorso = "/percorso/del/file/immagine.jpg" Print "File immagine JPEG: ", Null; percorso ' Carica i dati jpeg da un file in un buffer di memoria: dim_jpg = Stat(percorso).Size Print "Dimensione del file: ", Null; dim_jpg; " byte" fl = Open percorso For Read With buffer = New Byte[dim_jpg] .Read(fl, 0, buffer.Max) End With fl.close() mgr = Alloc(168) cinfo = Alloc(656) st = Memory cinfo For Write Write #st, jpeg_std_error(mgr) As Pointer jpeg_CreateDecompress(cinfo, 80, 656) jpeg_mem_src(cinfo, buffer, dim_jpg) rc = jpeg_read_header(cinfo, True) If rc <> 1 Then Error.Raise("Il file non sembra essere un normale JPEG !") ' Comincia la decompressione dei dati JPEG: jpeg_start_decompress(cinfo) Seek #st, 48 Read #st, width Print "Larghezza in pixel: ", Null; width Read #st, height Print "Altezza in pixel: ", Null; height Seek #st, 56 Read #st, dim_pixel Print "Byte per pixel: ", Null; pixel_size; " (profondità: "; dim_pixel * 8; " bit)" st.Close dim_bmp = width * height * dim_pixel Print "Dimensione bitMap: ", Null; dim_bmp; " byte\n" ' La variabile "prog_riga" è il numero totale di byte che serve per memorizzare un'intera linea di scansione (riga): prog_riga = width * dim_pixel ' Al termine della decompressione, si va a leggere tutte le linee di scansione del jpeg, ' le quali di norma risultano disposte con l'ordine RGBRGBRGB (ordine che può essere cambiato agendo sul membro "cinfo.out_color_space"): bmp = Legge_linee(cinfo, dim_bmp, prog_riga) jpeg_finish_decompress(cinfo) jpeg_destroy_decompress(cinfo) ' Ottenuti i dati grezzi, possiamo utilizzarli, ad esempio creando un file immagine non compresso (in questo caso di formato PNM): fl = Open "/tmp/file.ppm" For Create st = Memory bmp For Read bb = New Byte[dim_bmp] bb.Read(st) header = "P6 " & CStr(width) & " " & CStr(height) & " 255\n" With buf = Byte[].FromString(header) .Insert(bb) .Write(fl) End With ' Va in chiusura: st.Close fl.Close Free(cinfo) Free(mgr) End