2013-09-28 14 views
22

A partire da un'immagine, vorrei spostare il suo contenuto nella parte superiore di 10 pixel, senza modificare le dimensioni e riempire la sotto immagine width x 10 in basso con il nero.Spostare il contenuto dell'immagine con OpenCV

Per esempio, l'originale:

Original

E il spostato:

Shifted

C'è una funzione per eseguire questa operazione direttamente con OpenCV?

risposta

25

Esiste una funzione per eseguire direttamente questa operazione con OpenCV?

http://code.opencv.org/issues/2299

o si dovrebbe fare questo

cv::Mat out = cv::Mat::zeros(frame.size(), frame.type()); 
    frame(cv::Rect(0,10, frame.cols,frame.rows-10)).copyTo(out(cv::Rect(0,0,frame.cols,frame.rows-10))); 
+0

Questo è un trucco un po 'sgradevole. Funziona, ma non è necessario creare "zero image" e copiare. Credo che la mia soluzione con [trasformazione affine] (http://stackoverflow.com/a/26766505/2740907) sia più pulita e prestazioni migliori. –

+8

@pajus_cz, "zero image" verrà creato in entrambi i casi, anche se non lo si sta creando da soli. Inoltre, copyTo esegue un'operazione di memcopy semplice molto più veloce di tutte le operazioni complesse eseguite da wrapAffine. – user2983637

+0

Grazie! Il secondo risultato è più veloce di wrapAffine per me. – dhiga

0

è possibile utilizzare un semplice filtro 2d/convoluzione per raggiungere il tuo obiettivo:

Preso direttamente dal the OpenCV documentation. Dovrai filtrare con un kernel che ha altezza (desired_displacement_y * 2 + 1) e width (desired_displacement_x * 2 + 1).

Quindi sarà necessario impostare il kernel su tutti gli zeri tranne la relativa posizione dei pixel da cui si desidera copiare. Quindi se il centro del tuo kernel è (0,0) devi impostare (10,0) su 1 per uno spostamento di 10 pixel.

prendere il codice di esempio dal sito web, e sostituire il codice del kernel in mezzo con il seguente:

/// Update kernel size for a normalized box filter 
    kernel_size = 1 + ind * 2; //Center pixel plus displacement diameter (=radius * 2) 
    kernel = Mat::zeros(kernel_size, kernel_size, CV_32F); 
    kernel.at<float>(ind * 2, ind) = 1.0f; // Indices are zero-based, not relative 

    /// Apply filter 
    filter2D(src, dst, ddepth , kernel, anchor, delta, BORDER_CONSTANT); 

Avviso BORDER_CONSTANT in filter2D! Ora dovresti eseguire l'esempio e far scorrere l'immagine di un pixel ogni 0,5 secondi. Puoi anche disegnare i pixel neri usando i metodi di disegno.

Perché funziona, vedere Wikipedia.

+0

Risposta creativa, ma intensiva della CPU. Usa invece il metodo Shift. – TimZaman

4

Ecco una funzione che ho scritto, in base alla risposta del Zaw Lin, di fare telaio/spostamento dell'immagine in qualsiasi direzione per qualsiasi quantità di pixel righe o colonne:

enum Direction{ 
    ShiftUp=1, ShiftRight, ShiftDown, ShiftLeft 
    }; 

cv::Mat shiftFrame(cv::Mat frame, int pixels, Direction direction) 
{ 
    //create a same sized temporary Mat with all the pixels flagged as invalid (-1) 
    cv::Mat temp = cv::Mat::zeros(frame.size(), frame.type()); 

    switch (direction) 
    { 
    case(ShiftUp) : 
     frame(cv::Rect(0, pixels, frame.cols, frame.rows - pixels)).copyTo(temp(cv::Rect(0, 0, temp.cols, temp.rows - pixels))); 
     break; 
    case(ShiftRight) : 
     frame(cv::Rect(0, 0, frame.cols - pixels, frame.rows)).copyTo(temp(cv::Rect(pixels, 0, frame.cols - pixels, frame.rows))); 
     break; 
    case(ShiftDown) : 
     frame(cv::Rect(0, 0, frame.cols, frame.rows - pixels)).copyTo(temp(cv::Rect(0, pixels, frame.cols, frame.rows - pixels))); 
     break; 
    case(ShiftLeft) : 
     frame(cv::Rect(pixels, 0, frame.cols - pixels, frame.rows)).copyTo(temp(cv::Rect(0, 0, frame.cols - pixels, frame.rows))); 
     break; 
    default: 
     std::cout << "Shift direction is not set properly" << std::endl; 
    } 

    return temp; 
} 
26

si può semplicemente utilizzare matrice di traduzione trasformazione affine (che è per spostare poi fondamentalmente). cv::warpAffine() con la matrice di trasformazione corretta farà il trucco.

TranslationMatrix [1,0,tx ; 0,1,ty]

dove: tx è spostamento dell'asse x immagine, ty è spostamento dell'asse immagine y, Ogni singolo pixel nell'immagine verrà spostata così.

È possibile utilizzare questa funzione che restituisce la matrice di traduzione.(Questo probabilmente non è necessario per te) Ma sposta l'immagine in base ai parametri offsetx e offsety.

Mat translateImg(Mat &img, int offsetx, int offsety){ 
    Mat trans_mat = (Mat_<double>(2,3) << 1, 0, offsetx, 0, 1, offsety); 
    warpAffine(img,img,trans_mat,img.size()); 
    return trans_mat; 
} 

Nel tuo caso - si desidera spostare immagine 10 pixel up, si chiama:

translateImg(image,0,-10); 

E allora la vostra immagine verrà spostata come volete.

+0

Puoi spiegare cosa dice la frase 'Mat_ (2,3) << 1'? – locke14

+1

Non è solo 'Mat_ (2,3) << 1'. È l'intera espressione: 'Mat_ (2,3) << 1, 0, offsetx, 0, 1, offsety' che inserisce i valori nella matrice. Quindi i valori in 'trans_mat' saranno' 1,0, offsetx' nella prima riga e '0, 1, offsety' nella seconda riga. Come è mostrato come matrice 'M' nel post originale. –

+1

Risposta creativa, ma intensiva della CPU rispetto alle altre soluzioni. – TimZaman

1

mia implementazione utilizza lo stesso come la risposta accettata ma può muoversi in qualsiasi direzione ...

using namespace cv; 
//and whatever header 'abs' requires... 

Mat offsetImageWithPadding(const Mat& originalImage, int offsetX, int offsetY, Scalar backgroundColour){ 
     cv::Mat padded = Mat(originalImage.rows + 2 * abs(offsetY), originalImage.cols + 2 * abs(offsetX), CV_8UC3, backgroundColour); 
     originalImage.copyTo(padded(Rect(abs(offsetX), abs(offsetY), originalImage.cols, originalImage.rows))); 
     return Mat(padded,Rect(abs(offsetX) + offsetX, abs(offsetY) + offsetY, originalImage.cols, originalImage.rows)); 
} 

//example use with black borders along the right hand side and top: 
Mat offsetImage = offsetImageWithPadding(originalImage, -10, 6, Scalar(0,0,0)); 

E 'tratto dal mio codice di lavoro, ma alcune variabili cambiato, se esso doesn' t compilare, molto probabilmente solo una piccola cosa ha bisogno di essere cambiata, ma tu hai l'idea di farlo. la funzione abs ...

0

Ho provato per la prima volta con pajus_cz's answer, ma in pratica era piuttosto lento. Inoltre, non posso permettermi di fare una copia temporanea, così mi si avvicinò con questo:

void translateY(cv::Mat& image, int yOffset) 
{ 
    int validHeight = std::max(image.rows - abs(yOffset), 0); 
    int firstSourceRow = std::max(-yOffset, 0); 
    int firstDestinationRow = std::max(yOffset, 0); 

    memmove(image.ptr(firstDestinationRow), 
      image.ptr(firstSourceRow), 
      validHeight * image.step); 
} 

ordini è tutto di grandezza più veloce rispetto alla soluzione basata su warpAffine. (Ma questo, naturalmente, può essere del tutto irrilevante nel tuo caso.)

2

C'è una funzione per eseguire questa operazione direttamente con OpenCV?

http://code.opencv.org/issues/2299

o si dovrebbe fare questo

cv::Mat out = cv::Mat::zeros(frame.size(), frame.type()); 
    frame(cv::Rect(0,10, 
    frame.cols,frame.rows-10)).copyTo(out(cv::Rect(0,0,frame.cols,frame.rows-10))); 

Il codice di cui sopra solo può essere usato per spostare da un lato (a sinistra, e verso l'alto). Sotto codice è la versione estesa del codice sopra che può essere utilizzato per spostare in ogni direzione.

int shiftCol = 10; 
int shiftRow = 10; 

Rect source = cv::Rect(max(0,-shiftCol),max(0,-shiftRow), frame.cols-abs(shiftCol),frame.rows-abs(shiftRow)); 

Rect target = cv::Rect(max(0,shiftCol),max(0,shiftRow),frame.cols-abs(shiftCol),frame.rows-abs(shiftRow)); 

frame(source).copyTo(out(target)); 
Problemi correlati