Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

OpenCv: Translate Image, Wrap Pixels Around Edges (C++)

I am trying to translate an image horizontally by x pixels and vertically by y pixels, so . However, I want the pixels to wrap around the edges. Basically...

we start with image one...

shift by x pixels...

and shift by y pixels...

As far as I know, OpenCv's warpAffine() is not capable of doing this. Normally, I would just loop through the image and shift the pixels a certain amount, but in doing this I can only shift them horizontally. What is the most efficient way to go about this?

like image 518
poppy Avatar asked Jan 25 '23 19:01

poppy


2 Answers

You can use np.roll()

Here's a visualization

I implemented it in Python but you can apply a similar rolling technique in C++

import cv2
import numpy as np

image = cv2.imread('1.jpg')

shift_x = 800
shift_y = 650

# Shift by x-axis
for i in range(image.shape[1] -1, shift_x, -1):
    image = np.roll(image, -1, axis=1)
    image[:, -1] = image[:, 0]
    cv2.imshow('image', image)
    cv2.waitKey(1)

# Shift by y-axis
for i in range(image.shape[1] -1, shift_y, -1):
    image = np.roll(image, -1, axis=0)
    image[:, -1] = image[:, 0]
    cv2.imshow('image', image)
    cv2.waitKey(1)

cv2.imshow('image', image)
cv2.waitKey()
like image 177
nathancy Avatar answered Jan 31 '23 06:01

nathancy


From my point of view, the most "efficient" way would be to set up the four corresponding ROIs by using cv::Rect, and manually copying the contents using cv::copyTo. Maybe, there's also a possibility without copying the actual content, just pointing to the data in the input cv::Mat - but unfortunately, at least I couldn't found one.

Nevertheless, here's my code:

// Shift input image by sx pixels to the left, and sy pixels to the top.
cv::Mat transWrap(cv::Mat& input, const int sx, const int sy)
{
    // Get image dimensions.
    const int w = input.size().width;
    const int h = input.size().height;

    // Initialize output with same dimensions and type.
    cv::Mat output = cv::Mat(h, w, input.type());

    // Copy proper contents manually.
    input(cv::Rect(sx, sy, w - sx, h - sy)).copyTo(output(cv::Rect(0, 0, w - sx, h - sy)));
    input(cv::Rect(0, sy, sx, h - sy)).copyTo(output(cv::Rect(w - sx, 0, sx, h - sy)));
    input(cv::Rect(sx, 0, w - sx, sy)).copyTo(output(cv::Rect(0, h - sy, w - sx, sy)));
    input(cv::Rect(0, 0, sx, sy)).copyTo(output(cv::Rect(w - sx, h - sy, sx, sy)));

    return output;
}

int main()
{
    cv::Mat input = cv::imread("images/tcLUa.jpg", cv::IMREAD_COLOR);
    cv::resize(input, input, cv::Size(), 0.25, 0.25);
    cv::Mat output = transWrap(input, 300, 150);

    return 0;
}

Of course, the code seems repetitive, but wrapped into an own function, it won't bother you in your main code. ;-)

The output should be, what you want to achieve:

Output

Hope that helps!

like image 34
HansHirse Avatar answered Jan 31 '23 05:01

HansHirse