web-dev-qa-db-fra.com

Décaler le contenu de l'image avec OpenCV

A partir d'une image, je voudrais déplacer son contenu vers le haut de 10 pixels, sans changer la taille et remplir la sous-image width x 10 en bas avec du noir.

Par exemple, l'original:

Original

Et les décalés:

Shifted

Existe-t-il une fonction pour effectuer directement cette opération avec OpenCV?

27
vdenotaris

Existe-t-il une fonction pour effectuer directement cette opération avec OpenCV?

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

ou tu ferais ça

    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)));
25
Zaw Lin

Vous pouvez simplement utiliser une matrice de traduction de transformation affine (qui sert essentiellement à déplacer des points). cv::warpAffine() avec une matrice de transformation appropriée fera l'affaire.

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

où: tx est le décalage dans l'axe x de l'image, ty est le décalage dans l'axe y de l'image, chaque pixel de l'image sera décalé comme ça.

Vous pouvez utiliser cette fonction qui retourne la matrice de traduction. (Cela n'est probablement pas nécessaire pour vous) Mais cela décalera l'image en fonction des paramètres offsetx et 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 img;
}

Dans votre cas - vous souhaitez décaler l'image de 10 pixels vers le haut, vous appelez:

translateImg(image,0,-10);

Et puis votre image sera déplacée comme vous le souhaitez.

31
pajus_cz

Voici une fonction que j'ai écrite, basée sur la réponse de Zaw Lin, pour effectuer un décalage de trame/image dans n'importe quelle direction par n'importe quelle quantité de lignes ou de colonnes de pixels:

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;
}
4
Antonis Io

Existe-t-il une fonction pour effectuer directement cette opération avec OpenCV?

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

ou tu ferais ça

  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)));

Le code ci-dessus ne peut être utilisé que pour se déplacer d'un côté (vers la gauche et vers le haut). Le code ci-dessous est la version étendue du code ci-dessus qui peut être utilisée pour se déplacer dans toutes les directions.

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));
3
William Sutrisna

Mon implémentation utilise la même chose que la réponse acceptée, mais elle peut se déplacer dans la direction ...

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));

Il est tiré de mon propre code de travail, mais certaines variables ont changé, si elles ne se compilent pas, très probablement une petite chose doit être changée - mais vous avez l'idée de nouveau. la fonction abs ...

1
Lamar Latrell

Vous pouvez utiliser un simple filtre/convolution 2D pour atteindre votre objectif:

Tiré directement de la documentation OpenCV . Vous devrez filtrer avec un noyau qui a une hauteur (desire_displacement_y * 2 + 1) et une largeur (desire_displacement_x * 2 + 1).

Ensuite, vous devrez définir le noyau sur tous les zéros à l'exception de la position relative des pixels d'où vous souhaitez copier. Donc, si le centre de votre noyau est (0,0), vous mettriez (10,0) à 1 pour un déplacement de 10 pixels.

Prenez l'exemple de code du site Web et remplacez le code du noyau au milieu par ce qui suit:

  /// 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 );

Remarquez BORDER_CONSTANT dans filter2D! Vous devez maintenant exécuter l'exemple et faire défiler l'image d'un pixel vers le haut toutes les 0,5 secondes. Vous pouvez également dessiner les pixels noirs en utilisant des méthodes de dessin.

Pour savoir pourquoi cela fonctionne, voir Wikipedia .

0
FRob

J'ai d'abord essayé avec réponse de pajus_cz , mais c'était assez lent en pratique. De plus, je ne peux pas me permettre de faire une copie temporaire, alors j'ai trouvé ceci:

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);
}

C'est des ordres de grandeur plus rapides que la solution basée sur warpAffine. (Mais cela peut bien sûr être complètement hors de propos dans votre cas.)

0
Reunanen