2013-06-11 5 views
8

Sono un principiante nell'uso di OpenCV per JAVA. Voglio accedere ai singoli valori dei pixel di una matrice di immagini. Dal momento che JAVA jar per OpenCV non offre funzioni piacevoli come il C++, mi sono imbattuto in qualche problema. Dopo molte ricerche, ho scoperto due metodi diversi per farlo anche se non sono spiegati correttamente (nemmeno nella documentazione). Possiamo farlo usando le funzioni get() e put() o convertendo i dati mat in un tipo java primitivo come gli array. Ho provato entrambi, ma ho ottenuto risultati di output diversi! Per favore aiutami a spiegare cosa sto sbagliando. Sto usando male o qualche altro stupido problema. Sono ancora un principiante quindi per favore perdonare se è una domanda stupida. :)Utilizzo di get() e put() per accedere ai valori dei pixel in OpenCV per Java

CASO 1: Utilizzando la funzione get()

Mat A = Highgui.imread(image_addr); \\"image_addr" is the address of the image 
Mat C = A.clone(); 
Size sizeA = A.size(); 
for (int i = 0; i < sizeA.height; i++) 
    for (int j = 0; j < sizeA.width; j++) { 
     double[] data = A.get(i, j); 
     data[0] = data[0]/2; 
     data[1] = data[1]/2; 
     data[2] = data[2]/2; 
     C.put(i, j, data); 
    } 

CASO 2: utilizza la matrice

Mat A = Highgui.imread(image_addr); \\"image_addr" is the address of the image 
Mat C = A.clone(); 
int size = (int) (A.total() * A.channels()); 
byte[] temp = new byte[size]; 
A.get(0, 0, temp); 
for (int i = 0; i < size; i++) 
    temp[i] = (byte) (temp[i]/2); 
C.put(0, 0, temp); 

Ora, secondo la mia comprensione che entrambi dovrebbero fare la stessa cosa. Entrambi accedono ai valori dei singoli pixel (tutti e 3 i canali) e ne fanno la metà. Non ricevo errori dopo l'esecuzione. Ma l'immagine che ho ottenuto è diversa in questi due casi. Qualcuno può spiegare qual è il problema? Può essere che non capisco esattamente come funziona la funzione get()? È a causa del casting di byte()? Per favore aiuto.

Grazie!

risposta

9

Stava succedendo a causa del lancio di byte(). Ho cambiato il tipo di dati dell'immagine mat nel secondo caso in * CV_64FC3 * in modo da poter usare double [] invece di byte [] e ho risolto il problema.

Mat A = Highgui.imread(image_addr); //"image_addr" is the address of the image 
Mat C = A.clone(); 
A.convertTo(A, CvType.CV_64FC3); // New line added. 
int size = (int) (A.total() * A.channels()); 
double[] temp = new double[size]; // use double[] instead of byte[] 
A.get(0, 0, temp); 
for (int i = 0; i < size; i++) 
    temp[i] = (temp[i]/2); // no more casting required. 
C.put(0, 0, temp); 

Cordiali saluti, ho anche fatto qualche misurazione del tempo e l'utilizzo secondo metodo è il modo più veloce di primo metodo.

+0

grazie per la bella domanda, puoi spiegare perché hai usato: (A.total() * A.channels()); ?? il totale (metodo restituisce l'altezza withd *, perché è multiplicato dai numeri di canale, so che l'immagine RGB ha 3 canali ... ma ho bisogno di ulteriori chiarimenti per favore – rmaik

+0

@rmaik Ogni canale ha i propri valori di intensità (rosso, blu o verde) per ciascun pixel. Quindi, il numero totale di quei valori di intensità in un'immagine è uguale al numero di pixel * numero totale di canali. Quindi, in questo caso total() ti dà il numero totale di pixel nell'immagine e nel canale() ti dà il numero di canali – gargsl

1

trovato una soluzione semplice e di lavoro dopo un sacco di searching-

Mat img = Highgui.imread("Input.jpg"); //Reads image from the file system and puts into matrix 
int rows = img.rows(); //Calculates number of rows 
int cols = img.cols(); //Calculates number of columns 
int ch = img.channels(); //Calculates number of channels (Grayscale: 1, RGB: 3, etc.) 

for (int i=0; i<rows; i++) 
{ 
    for (int j=0; j<cols; j++) 
    { 
     double[] data = img.get(i, j); //Stores element in an array 
     for (int k = 0; k < ch; k++) //Runs for the available number of channels 
     { 
      data[k] = data[k] * 2; //Pixel modification done here 
     } 
     img.put(i, j, data); //Puts element back into matrix 
    } 
} 
Highgui.imwrite("Output.jpg", img); //Writes image back to the file system using values of the modified matrix 

Nota: Un punto importante che non è stato menzionato da nessuna parte online è che il metodo put non scrive pixel su Input.jpg. Aggiorna semplicemente i valori della matrice img. Pertanto, il codice precedente non modifica nulla nell'immagine di input. Per produrre un'uscita visibile, la matrice img deve essere scritta su un file, ad esempio Output.jpg in questo caso. Inoltre, l'utilizzo di img.get(i, j) sembra essere un modo migliore di gestire gli elementi di matrice piuttosto che utilizzare la soluzione accettata sopra poiché ciò aiuta a visualizzare e lavorare con la matrice di immagini in un modo migliore e non richiede una grande allocazione di memoria contigua.

+0

Funziona ma è molto molto lento. Può prendere 30 secondi per un'immagine da 8 MP su un buon computer. Invece vuoi leggere tutti i dati contemporaneamente in un buffer usando Mat.get (0,0, byte [totalNumberOfPixel * bytesPerPixel]) – RenaudBlue