Decomprimere un file immagine jpeg per ottenerne i dati grezzi mediante le funzioni esterne del API di libjpeg

Da Gambas-it.org - Wikipedia.

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 condivisa: "libjpeg.so.8.2.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.
Il progamma mostrerà anche alcune informazioni di carattere generale sul file JPEG caricato.

Library "libjpeg:8.2.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)


' unsigned char *  Legge_linee(struct jpeg_decompress_struct * cinfo, int bmp_size, int row_stride)
' Invoca la libreria externa appositamente creata per leggere le righe di pixel del file JPEG.
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 As Integer
 Dim num_comp, dim_pixel, rc As Integer
 Dim buffer, buf, bb As Byte[]

' Crea la libreria esterna condivisa .so appositamente scritta:
 Creaso()
  
 percorso = "/percorso/del/file/immagine.jpg"
 If Not Exist(percorso) Then Error.Raise("Percorso file immagine inesistente !")
 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: "; 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(Sizeof(gb.Byte), 168)
 cinfo = Alloc(Sizeof(gb.Byte), 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:  "; width
 Read #st, height
 Print "Altezza in pixel:    "; height
 Read #st, num_comp
 Read #st, dim_pixel
 Print "Byte per pixel:      "; num_comp; " (profondità: "; dim_pixel * 8; " bit)"
 st.Close
  
 dim_bmp = width * height * dim_pixel
 Print "Dimensione bitMap:   "; 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

Private Procedure Creaso()
 
' Imposta il codice sorgente della parte in linguaggio C:
 File.Save("/tmp/libadhoc.c", "#include <stdio.h>\n#include <stdlib.h>\n#include <jpeglib.h>\n\n" &
           "unsigned char * bmp_buffer;\n\n" &
           "unsigned char *  Legge_linee(struct jpeg_decompress_struct * cinfo, int bmp_size, int row_stride) {\n\n" &
           "   bmp_buffer = (unsigned char*) malloc(bmp_size);\n\n" &
           "   while (cinfo->output_scanline < cinfo->output_height) {\n" &
           "     unsigned char *buffer_array[1];\n" &
           "     buffer_array[0] = bmp_buffer + (cinfo->output_scanline) * row_stride;\n" &
           "     jpeg_read_scanlines(cinfo, buffer_array, 1);\n   }\n\n" &
           "   return bmp_buffer;\n\n}")
 
' Crea la libreria esterna condivisa .so appositamente scritta per gestire la parte in linguaggio C:
 Shell "gcc -o /tmp/libadhoc.so /tmp/libadhoc.c -shared -fPIC -ljpeg" Wait
 
End


Riferimenti