2015-01-17 9 views
6

Per essere onesti, sono sorpreso che nessuno si sia imbattuto fino a questo punto. Sto caricando un'immagine da OpenCV in cv :: Mat, che voglio codificare in base64 prima di inviarlo su un socket.OpenCV cv :: Mat to std: ifstream per la codifica base64

Per base64 sto usando libb64 dato che è nativo di Debian/Ubuntu e facile da usare e molto veloce. La funzione di codifica assume come parametro uno std :: ifstream e genera uno std :: ofstream.

#include <opencv2/opencv.hpp> 
#include <b64/encode.h> 
#include <fstream> 

using namespace cv; 
Mat image; 
image = imread("picture.jpg", CV_LOAD_IMAGE_COLOR); 

if (image.data) 
{ 
    std::ifstream instream(???, std::ios_base::in | std::ios_base::binary); 
    std::ofstream outstream;   

    // Convert Matrix to ifstream 
    // ... 

    base64::encoder E; 
    E.encode(instream, outstream); 

    // Now put it in a string, and send it over a socket... 
} 

Non so davvero come popolare lo instream dal cv :: Mat. Googling in giro, ho trovato che posso iterare un cv :: Mat, da colonne e righe, e ottenere ogni (pixel Io parto dal presupposto) valori RGB:

for (int j = 0; j < image.rows; j++) 
{ 
    for (int i = 0; i < image.cols; i++) 
    { 
     unsigned char b = input [ image.step * j + i ] ; 
     unsigned char g = input [ image.step * j + i + 1 ]; 
     unsigned char r = input [ image.step * j + i + 2 ]; 
    } 
} 

E 'questo il modo giusto di andare avanti su di esso ? C'è un modo più elegante?

+0

Cosa stai cercando di realizzare alla fine? Vuoi serializzare l'immagine (vuoi poterla leggere dall'altra parte)? Se si codificano solo i dati dell'immagine, non si conoscono la larghezza, l'altezza o il numero di canali. Perché non basi64 codificare il file di input invece del Mat? Inoltre, se si desidera inviare l'immagine su un socket, non è necessario codificarlo base64, è sufficiente inviarlo così com'è e ridurre il sovraccarico. – Ove

+0

@ L'altra estremità sul socket può essere diversa per architettura o piattaforma e non utilizzerà C++. Speravo di evitare certi problemi serializzando con base64, ma sono aperto a qualsiasi suggerimento. Quindi, pensi che sia meglio serializzare la foto invece del cv :: Mat? Il problema è che potrebbe non esserci alcuna immagine, ma un'immagine catturata da una telecamera. Inoltre, il socket utilizza il protocollo HTTP, quindi sto postando testo JSON. –

risposta

7

Per poter inviare un'immagine tramite HTTP, è inoltre necessario codificarne larghezza, altezza e tipo. È necessario serializzare lo Mat in uno stream e codificare quel flusso con libb64. Dall'altra parte è necessario decodificare quel flusso e deserializzare l'immagine per recuperarla.

Ho implementato un piccolo programma di test che esegue questa serializzazione e deserializzazione utilizzando std::stringstream come buffer. L'ho scelto perché estende sia std::istream e std::ostream che libb64 utilizza.

La funzione serialize serializza un cv::Mat in un std::stringstream. In esso, scrivo la larghezza, l'altezza, il tipo, la dimensione del buffer e il buffer stesso.

La funzione deserialize esegue il contrario. Legge la larghezza, l'altezza, il tipo, la dimensione del buffer e il buffer. Non è così efficiente come potrebbe essere perché ha bisogno di allocare un buffer temporaneo per leggere i dati dallo stringstream. Inoltre, ha bisogno di clonare l'immagine in modo che non faccia affidamento sul buffer temporaneo e gestirà la propria allocazione di memoria. Sono sicuro che con qualche ritocco può essere reso più efficiente.

La funzione principale carica un'immagine, la serializza, la codifica utilizzando libb64, quindi la decodifica, la deserializza e la visualizza in una finestra. Questo dovrebbe simulare ciò che stai cercando di fare.

// Serialize a cv::Mat to a stringstream 
stringstream serialize(Mat input) 
{ 
    // We will need to also serialize the width, height, type and size of the matrix 
    int width = input.cols; 
    int height = input.rows; 
    int type = input.type(); 
    size_t size = input.total() * input.elemSize(); 

    // Initialize a stringstream and write the data 
    stringstream ss; 
    ss.write((char*)(&width), sizeof(int)); 
    ss.write((char*)(&height), sizeof(int)); 
    ss.write((char*)(&type), sizeof(int)); 
    ss.write((char*)(&size), sizeof(size_t)); 

    // Write the whole image data 
    ss.write((char*)input.data, size); 

    return ss; 
} 

// Deserialize a Mat from a stringstream 
Mat deserialize(stringstream& input) 
{ 
    // The data we need to deserialize 
    int width = 0; 
    int height = 0; 
    int type = 0; 
    size_t size = 0; 

    // Read the width, height, type and size of the buffer 
    input.read((char*)(&width), sizeof(int)); 
    input.read((char*)(&height), sizeof(int)); 
    input.read((char*)(&type), sizeof(int)); 
    input.read((char*)(&size), sizeof(size_t)); 

    // Allocate a buffer for the pixels 
    char* data = new char[size]; 
    // Read the pixels from the stringstream 
    input.read(data, size); 

    // Construct the image (clone it so that it won't need our buffer anymore) 
    Mat m = Mat(height, width, type, data).clone(); 

    // Delete our buffer 
    delete[]data; 

    // Return the matrix 
    return m; 
} 

void main() 
{ 
    // Read a test image 
    Mat input = imread("D:\\test\\test.jpg"); 

    // Serialize the input image to a stringstream 
    stringstream serializedStream = serialize(input); 

    // Base64 encode the stringstream 
    base64::encoder E; 
    stringstream encoded; 
    E.encode(serializedStream, encoded); 

    // Base64 decode the stringstream 
    base64::decoder D; 
    stringstream decoded; 
    D.decode(encoded, decoded); 

    // Deserialize the image from the decoded stringstream 
    Mat deserialized = deserialize(decoded); 

    // Show the retrieved image 
    imshow("Retrieved image", deserialized); 
    waitKey(0); 
} 
+0

Wow, grazie mille! Non mi aspettavo di essere alimentato a cucchiaio la risposta :-) –

+0

Questo codice sembra obsoleto, utilizza le utilità stringstream cancellate –