可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
Starting from an image, I would like to shift its content to the top of 10 pixels, without change the size and filling the sub image width x 10
on bottom with black.
For instance, the original:
And the shifted:
Is there a function to perform directly this operation with OpenCV?
回答1:
Is there a function to perform directly this operation with OpenCV?
http://code.opencv.org/issues/2299
or you would do this
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)));
回答2:
You can simply use affine transformation translation matrix (which is for shifting points basically). cv::warpAffine()
with proper transformation matrix will do the trick.

where: tx is shift in the image x axis, ty is shift in the image y axis, Every single pixel in the image will be shifted like that.
You can use this function which returns the translation matrix. (That is probably unnecessary for you) But it will shift the image based on offsetx
and offsety
parameters.
Mat translateImg(Mat &img, int offsetx, int offsety){ Mat trans_mat = (Mat_(2,3)
In your case - you want to shift image 10 pixels up, you call:
translateImg(image,0,-10);
And then your image will be shifted as you desire.
回答3:
Here is a function I wrote, based on Zaw Lin's answer, to do frame/image shift in any direction by any amount of pixel rows or columns:
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
回答4:
Is there a function to perform directly this operation with OpenCV?
http://code.opencv.org/issues/2299
or you would do this
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)));
The code above only can be used to shift to one side (to the left, and to the top). Below code is the extended version of above code which can be used to shift into every direction.
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));
回答5:
My implementation uses the same as the accepted answer however it can move in any 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));
It's taken from my own working code but some variables changed, if it doesn't compile, very likely just a small thing needs changing - but you get the idea re. the abs function...
回答6:
You can use a simple 2d filter/convolution to achieve your goal:
Taken straight from the OpenCV documentation. You will need to filter with a kernel that has height (desired_displacement_y * 2 + 1) and width (desired_displacement_x * 2 + 1).
Then you will need to set the kernel to all zeros except for the relative pixel position from where you want to copy. So if your kernel center is (0,0) you would set (10,0) to 1 for a displacement of 10 pixels.
Take the sample code from the website, and replace the kernel code in the middle with the following:
/// 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(ind * 2, ind) = 1.0f; // Indices are zero-based, not relative /// Apply filter filter2D(src, dst, ddepth , kernel, anchor, delta, BORDER_CONSTANT );
Notice BORDER_CONSTANT in filter2D! You should now run the example and have a the picture scroll up by one pixel every 0.5 seconds. You could also draw the black pixels using drawing methods.
On why this works, see Wikipedia.
回答7:
I first tried with pajus_cz's answer, but it was quite slow in practice. Also, I cannot afford to make a temporary copy, so I came up with this:
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); }
It's orders of magnitude faster than the warpAffine
-based solution. (But this of course may be completely irrelevant in your case.)