Image rotation by very small step size in openCV c++ - c++

I'm trying to rotate an image by small degrees like 1 degree or below 1 degree,
Consider this is my Source Image and I'm trying to rotate it by 1 degree in MATLAB and OpenCV (C++) :
0 0 0 0
0 255 255 0
0 255 255 0
0 0 0 0
When I rotate it in MATLAB by 1 degree this is the result:
0 0 2.223835069639144 0
2.223835069639553 252.7942370468069 2.527942370468065 0
0 252.7942370468065 252.794237046807 2.223835069639648
0 2.223835069639184 0 0
This is the code in MATLAB:
sourceImage = [0 0 0 0; 0 255.0 255.0 0; 0 255.0 255.0 0; 0 0 0 0];
rotationDegree = 1.0;
shearInX_Y1 = (cosd(rotationDegree)-1)/sind(rotationDegree);
shearInX_Y2 = sind(rotationDegree);
transformationMatrix = [(1 + shearInX_Y1*shearInX_Y2), (2*shearInX_Y1 + ...
((shearInX_Y1)^2)*shearInX_Y2), 0; (shearInX_Y2), (1 + shearInX_Y1*shearInX_Y2), 0; 0, 0, 1];
tform = affine2d(transformationMatrix);
imref = imref2d(size(sourceImage));
imref.XWorldLimits = imref.XWorldLimits-mean(imref.XWorldLimits);
imref.YWorldLimits = imref.YWorldLimits-mean(imref.YWorldLimits);
transformedImg2 = imwarp(sourceImage , imref, tform, 'OutputView', imref, 'Interp', 'bilinear');
transformedImg2
The transformationMatrix in Matlab (which is our rotation matrix) is:
transformationMatrix =
9.998476951563913e-01 -1.745240643728003e-02 0
1.745240643728351e-02 9.998476951563913e-01 0
0 0 1.000000000000000e+00
And the content of "tform.T" is:
9.998476951563913e-01 -1.745240643728003e-02 0
1.745240643728351e-02 9.998476951563913e-01 0
0 0 1
And the content of "rotationMatrix" in OpenCV is:
9.998476951563913e-01 1.745240643728351e-02 -0.008650050796837391
-1.745240643728351e-02 9.998476951563913e-01 0.008802355640446121
But when I do the rotation by 1 degree in OpenCV (C++) This is the result:
(which is same as source image!) which means openCV has a problem with small rotations!
0 0 0 0
0 255 255 0
0 255 255 0
0 0 0 0
This is the code I use for rotation in OpenCV (C++): (Rotation is done with respect to image center)
#include <opencv2/highgui/highgui.hpp>
#include <iostream>
using namespace cv;
using namespace std;
# define INTERPOLATION_METHOD INTER_CUBIC
// INTER_AREA
// INTER_LINEAR
// INTER_NEAREST
Mat rotateImage(Mat sourceImage, double rotationDegree);
int main(){
Mat sourceImage = Mat::zeros(4, 4, CV_64F);
sourceImage.at<double>(1, 1) = 255.0;
sourceImage.at<double>(1, 2) = 255.0;
sourceImage.at<double>(2, 1) = 255.0;
sourceImage.at<double>(2, 2) = 255.0;
double rotationDegree = 1.0;
Mat rotatedImage = rotateImage(sourceImage, rotationDegree);
cout << "sourceImage: \n" << sourceImage << endl << endl;
cout << "rotatedImage : \n" << rotatedImage << endl << endl;
return 0;
}
Mat rotateImage(Mat sourceImage, double rotationDegree){
double rowOfImgCenter;
double colOfImgCenter;
rowOfImgCenter = sourceImage.rows / 2.0 - 0.5;
colOfImgCenter = sourceImage.cols / 2.0 - 0.5;
Point2d imageCenter(colOfImgCenter, rowOfImgCenter);
Mat rotationMatrix;
rotationMatrix = getRotationMatrix2D(imageCenter, rotationDegree, 1.0);
Mat rotatedImage;
warpAffine(sourceImage, rotatedImage, rotationMatrix, sourceImage.size(), INTERPOLATION_METHOD);
return rotatedImage;
}
Any idea would be appreciated.

Related

Problem with rotating an object with face to another one. [C++]

I want to rotate an object with the face side to the center of another one, but I have some problems with it: when I try to rotate an object to another one and it lies on X axis, it works properly [first two screenshots], but when I try to rotate it as on the screenshot, everything breaks down [second two screenshots].
Before1:
1 0 0 0
0 1 0 0
0 0 1 0
0 0 0 1
After1:
0 0 -1 0
-0 -1 0 0
1 0 0 0
0 0 0 1
Before2:
0 0 -1 0
-0 -1 0 0
1 0 0 0
0 0 0 1
After2:
0 0 -0.707107 0
0.5 -0.5 0 0
0.707107 -0.707107 0 0
0 0 0 1
Here's my code:
void ConcreteObject::faceObjectTo(ConcreteObject otherObject) {
Vector<double> temp = {0, 1, 0};
Vector<double> forward = otherObject.getCenter() - this->getCenter();
forward.normalize();
Vector<double> right = temp.cross(forward);
right.normalize();
Vector<double> up = forward.cross(right);
Matrix<double> newMatrix = this->getTransformMatrix().getCurrentState();
newMatrix(0, 0) = right[0];
newMatrix(0, 1) = right[1];
newMatrix(0, 2) = right[2];
newMatrix(1, 0) = up[0];
newMatrix(1, 1) = up[1];
newMatrix(1, 2) = up[2];
newMatrix(2, 0) = forward[0];
newMatrix(2, 1) = forward[1];
newMatrix(2, 2) = forward[2];
TransformMatrix newObjectMatrix(newMatrix);
this->setTransformMatrix(newObjectMatrix);
}
You need to normalize right, there's no reason for temp and forward to be orthogonal, hence even if they are unit vectors, their crossproduct need not be.

How to implement the imrotate functioin using OpenCV

Yes. I want to get the same result like the imrotate in matlab, but I can't get the point.
More specifically,I get a mat A
a =
1 2 3
4 5 6
imrotate(a, 45)
ans =
0 0 0 0 0
0 0 3 6 0
0 1 2 6 0
0 1 4 0 0
0 0 0 0 0
How can I do that using OpenCV?
You may need use getRotationMatrix2D and warpAffine.
like this:
Mat A(5, 5, CV_8U, Scalar(0));
Point center(3, 3);
double angle = 45;
int value=1;
for (int i = 2 ; i<4 ;i++)
{
for (int j = 1 ; j <4 ;j++)
{
A.at(j,i) = value;
value++;
}
}
Mat matrix = getRotationMatrix2D(center, angle, 1.0);
Mat imgRotation;
warpAffine(A, imgRotation, matrix, Size(5, 5));

Swap two colors using color matrix

How can I swap two colors using a color matrix? For instance swapping red and blue is easy. The matrix would look like:
0 0 1 0 0
0 1 0 0 0
1 0 0 0 0
0 0 0 1 0
0 0 0 0 1
So how can I swap any two colors in general? For example, there is Color1 with R1, G1, B1 and Color2 with R2, G2, B2.
EDIT: By swap I mean Color1 will translate into color2 and color2 will translate into color1. Looks like I need a reflection transformation. How to calculate it?
GIMP reference removed. Sorry for confusion.
This appears to be the section of the color-exchange.c file in the GIMP source that cycles through all the pixels and if a pixel meets the chosen criteria(which can be a range of colors), swaps it with the chosen color:
for (y = y1; y < y2; y++)
{
gimp_pixel_rgn_get_row (&srcPR, src_row, x1, y, width);
for (x = 0; x < width; x++)
{
guchar pixel_red, pixel_green, pixel_blue;
guchar new_red, new_green, new_blue;
guint idx;
/* get current pixel-values */
pixel_red = src_row[x * bpp];
pixel_green = src_row[x * bpp + 1];
pixel_blue = src_row[x * bpp + 2];
idx = x * bpp;
/* want this pixel? */
if (pixel_red >= min_red &&
pixel_red <= max_red &&
pixel_green >= min_green &&
pixel_green <= max_green &&
pixel_blue >= min_blue &&
pixel_blue <= max_blue)
{
guchar red_delta, green_delta, blue_delta;
red_delta = pixel_red > from_red ?
pixel_red - from_red : from_red - pixel_red;
green_delta = pixel_green > from_green ?
pixel_green - from_green : from_green - pixel_green;
blue_delta = pixel_blue > from_blue ?
pixel_blue - from_blue : from_blue - pixel_blue;
new_red = CLAMP (to_red + red_delta, 0, 255);
new_green = CLAMP (to_green + green_delta, 0, 255);
new_blue = CLAMP (to_blue + blue_delta, 0, 255);
}
else
{
new_red = pixel_red;
new_green = pixel_green;
new_blue = pixel_blue;
}
/* fill buffer */
dest_row[idx + 0] = new_red;
dest_row[idx + 1] = new_green;
dest_row[idx + 2] = new_blue;
/* copy alpha-channel */
if (has_alpha)
dest_row[idx + 3] = src_row[x * bpp + 3];
}
/* store the dest */
gimp_pixel_rgn_set_row (&destPR, dest_row, x1, y, width);
/* and tell the user what we're doing */
if (!preview && (y % 10) == 0)
gimp_progress_update ((gdouble) y / (gdouble) height);
}
EDIT/ADDITION
Another way you could have transformed red to blue would be with this matrix:
1 0 0 0 0
0 1 0 0 0
0 0 1 0 0
0 0 0 1 0
-1 0 1 0 1
The only values that really matter are the bottom ones in this matrix.
This would be the same as saying subtract 255 from red, keep green the same, and then add 255 to blue. You could cut the alpha in half like so as well like so:
-1 0 1 -0.5 1
So (just like the gimp source) you just need to find the difference between your current color and your target color, for each channel, and then apply the difference. Instead of channel values from 0 to 255 you would use values from 0 to 1.
You could have changed it from red to green like so:
-1 1 0 0 1
See here for some good info:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms533875%28v=vs.85%29.aspx
Good luck.
I solved it by creating a reflection matrix via D3DXMatrixReflect using a plane that's perpendicular to the vector AB and intersects the midpoint of the AB.
D3DXVECTOR3 AB( colorA.r-colorB.r, colorA.g-colorB.g, colorA.b-colorB.b );
D3DXPLANE plane( AB.x, AB.y, AB.z, -AB.x*midpoint.x-AB.y*midpoint.y-AB.z*midpoint.z );
D3DXMatrixReflect

Better ways to create a rectangular mask by openCV

Creating a mask in openCV
/** result I want
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 1 1 1 1 0 0
0 0 1 1 1 1 0 0
0 0 1 1 1 1 0 0
0 0 1 1 1 1 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
*/
cv::Mat mask = cv::Mat::zeros(8, 8, CV_8U);
std::cout<<"before : \n"<<mask<<std::endl;
for(int i = 2; i != 6; ++i)
{
auto ptr = mask.ptr<uchar>(i) + 2;
for(int j = 0; j != 4; ++j)
{
*ptr++ = 1;
}
}
std::cout<<"after : \n"<<mask<<std::endl;
Do openCV provide us any build in function to create a mask like this?
It is trivial to create a function fot this task, but the function of openCV
always faster than naive handcrafted codes
sure, there's an easier way, use the roi operator:
cv::Mat mask = cv::Mat::zeros(8, 8, CV_8U); // all 0
mask(Rect(2,2,4,4)) = 1;
done!
If some one is looking for creating a non rectangular mask and then to apply it on the image then have a look here :
Mat& obtainIregularROI(Mat& origImag, Point2f topLeft, Point2f topRight, Point2f botLeft, Point2f botRight){
static Mat black(origImag.rows, origImag.cols, origImag.type(), cv::Scalar::all(0));
Mat mask(origImag.rows, origImag.cols, CV_8UC1, cv::Scalar(0));
vector< vector<Point> > co_ordinates;
co_ordinates.push_back(vector<Point>());
co_ordinates[0].push_back(topLeft);
co_ordinates[0].push_back(botLeft);
co_ordinates[0].push_back(botRight);
co_ordinates[0].push_back(topRight);
drawContours( mask,co_ordinates,0, Scalar(255),CV_FILLED, 8 );
origImag.copyTo(black,mask);
return black;
}
"black" is the image where we will finally obtain the result by cropping out the irregular ROI from the original image.
static Mat black(origImag.rows, origImag.cols, origImag.type(), cv::Scalar::all(0));
The "mask" is a Mat, initialized as the same size of original image and filled with 0.
Mat mask(origImag.rows, origImag.cols, CV_8UC1, cv::Scalar(0));
Putting the coordinates in ANTICLOCKWISE direction
vector< vector<Point> > co_ordinates;
co_ordinates.push_back(vector<Point>());
co_ordinates[0].push_back(topLeft);
co_ordinates[0].push_back(botLeft);
co_ordinates[0].push_back(botRight);
co_ordinates[0].push_back(topRight);
Now generating the mask actually
drawContours( mask,co_ordinates,0, Scalar(255),CV_FILLED, 8 );
At the end copy the masked portion/ROI from original image (origImag) and paste on the portion of ROI from the original image (using mask) into image named as "black"
origImag.copyTo(black,mask);

Image downscaling algorithm

Could you help me find the right algorithm for image resizing? I have an image of a number. The maximum size is 200x200, I need to get an image with size 15x15 or even less. The image is monochrome (black and white) and the result should be the same. That's the info about my task.
I've already tried one algorithm, here it is
// xscale, yscale - decrease/increase rate
for (int f = 0; f<=49; f++)
{
for (int g = 0; g<=49; g++)//49+1 - final size
{
xpos = (int)f * xscale;
ypos = (int)g * yscale;
picture3[f][g]=picture4[xpos][ypos];
}
}
But it won't work with the decrease of an image, which is my prior target.
Could you help me find an algorithm, which could solve that problem (quality mustn't be perfect, the speed doesn't even matter). Some information about it would be perfect too considering the fact I'm a newbie. Of course, a short piece of c/c++ code (or a library) will be perfect too.
Edit:
I've found an algorithm. Will it be suitable for compressing from 200 to 20?
The general approach is to filter the input to generate a smaller size, and threshold to convert to monochrome. The easiest filter to implement is a simple average, and it often produces OK results. The Sinc filter is theoretically the best but it's impractical to implement and has ringing artifacts which are often undesirable. Many other filters are available, such as Lanczos or Tent (which is the generalized form of Bilinear).
Here's a version of an average filter combined with thresholding. Assuming picture4 is the input with pixel values of 0 or 1, and the output is picture3 in the same format. I also assumed that x is the least significant dimension which is opposite to the usual mathematical notation, and opposite to the coordinates in your question.
int thumbwidth = 15;
int thumbheight = 15;
double xscale = (thumbwidth+0.0) / width;
double yscale = (thumbheight+0.0) / height;
double threshold = 0.5 / (xscale * yscale);
double yend = 0.0;
for (int f = 0; f < thumbheight; f++) // y on output
{
double ystart = yend;
yend = (f + 1) / yscale;
if (yend >= height) yend = height - 0.000001;
double xend = 0.0;
for (int g = 0; g < thumbwidth; g++) // x on output
{
double xstart = xend;
xend = (g + 1) / xscale;
if (xend >= width) xend = width - 0.000001;
double sum = 0.0;
for (int y = (int)ystart; y <= (int)yend; ++y)
{
double yportion = 1.0;
if (y == (int)ystart) yportion -= ystart - y;
if (y == (int)yend) yportion -= y+1 - yend;
for (int x = (int)xstart; x <= (int)xend; ++x)
{
double xportion = 1.0;
if (x == (int)xstart) xportion -= xstart - x;
if (x == (int)xend) xportion -= x+1 - xend;
sum += picture4[y][x] * yportion * xportion;
}
}
picture3[f][g] = (sum > threshold) ? 1 : 0;
}
}
I've now tested this code. Here's the input 200x200 image, followed by a nearest-neighbor reduction to 15x15 (done in Paint Shop Pro), followed by the results of this code. I'll leave you to decide which is more faithful to the original; the difference would be much more obvious if the original had some fine detail.
To properly downscale an image, you should divide your image up into square blocks of pixels and then use something like Bilinear Interpolation in order to find the right color of the pixel that should replace the NxN block of pixels you're doing the interpolation on.
Since I'm not so good at the math involved, I'm not going to try give you an example of how the code would like. Sorry :(
Since you're fine with using a library, you could look into the imagemagick C++ bindings.
You could also output the image in a simple format like a pbm, and then call the imagemagick command to resize it:
system("convert input.pbm -resize 10x10 -compress none output.pbm");
Sample output file (note: you don't need to use a new line for each row):
P1
20 20
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 0 0 0 0 0
0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 0 0 0 0 0
0 0 0 0 0 0 0 1 1 0 0 0 0 1 1 0 0 0 0 0
0 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 0 0 0 0
0 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 0 0 0 0
0 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 0 0 0 0
0 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 0 0 0 0
0 0 0 0 0 0 0 1 1 0 0 0 0 1 1 1 0 0 0 0
0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 0 0
0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
The output file:
P1
10 10
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 1 1 0 1 1 0
0 0 0 0 1 0 0 1 1 0 0 0 0 0 1 0 0 1 1 0 0 0 0 0 1 1 0 1 1 0 0 0 0 0 0 1 1 1 1
1 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0
I've found an implementation of a bilinear interpolaton. C code.
Assuming that:
a - a primary array (which we need to stretch/compress) pointer.
oldw - primary width
oldh - primary height
b - a secondary array (which we get after compressing/stretching) pointer
neww - secondary width
newh - seconday height
#include <stdio.h>
#include <math.h>
#include <sys/types.h>
void resample(void *a, void *b, int oldw, int oldh, int neww, int newh)
{
int i;
int j;
int l;
int c;
float t;
float u;
float tmp;
float d1, d2, d3, d4;
u_int p1, p2, p3, p4; /* nearby pixels */
u_char red, green, blue;
for (i = 0; i < newh; i++) {
for (j = 0; j < neww; j++) {
tmp = (float) (i) / (float) (newh - 1) * (oldh - 1);
l = (int) floor(tmp);
if (l < 0) {
l = 0;
} else {
if (l >= oldh - 1) {
l = oldh - 2;
}
}
u = tmp - l;
tmp = (float) (j) / (float) (neww - 1) * (oldw - 1);
c = (int) floor(tmp);
if (c < 0) {
c = 0;
} else {
if (c >= oldw - 1) {
c = oldw - 2;
}
}
t = tmp - c;
/* coefficients */
d1 = (1 - t) * (1 - u);
d2 = t * (1 - u);
d3 = t * u;
d4 = (1 - t) * u;
/* nearby pixels: a[i][j] */
p1 = *((u_int*)a + (l * oldw) + c);
p2 = *((u_int*)a + (l * oldw) + c + 1);
p3 = *((u_int*)a + ((l + 1)* oldw) + c + 1);
p4 = *((u_int*)a + ((l + 1)* oldw) + c);
/* color components */
blue = (u_char)p1 * d1 + (u_char)p2 * d2 + (u_char)p3 * d3 + (u_char)p4 * d4;
green = (u_char)(p1 >> 8) * d1 + (u_char)(p2 >> 8) * d2 + (u_char)(p3 >> 8) * d3 + (u_char)(p4 >> 8) * d4;
red = (u_char)(p1 >> 16) * d1 + (u_char)(p2 >> 16) * d2 + (u_char)(p3 >> 16) * d3 + (u_char)(p4 >> 16) * d4;
/* new pixel R G B */
*((u_int*)b + (i * neww) + j) = (red << 16) | (green << 8) | (blue);
}
}
}
Hope it will be useful for other users. But nevertheless I still doubth whether it will work in my situation (when not stratching, but compressing an array). Any ideas?
I think, you need Interpolation. There are a lot of algorithms, for example you can use Bilinear interpolation
If you use Win32, then StretchBlt function possibly help.
The StretchBlt function copies a bitmap from a source rectangle into a destination rectangle, stretching or compressing the bitmap to fit the dimensions of the destination rectangle, if necessary. The system stretches or compresses the bitmap according to the stretching mode currently set in the destination device context.
One approach to downsizing a 200x200 image to, say 100x100, would be to take every 2nd pixel along each row and column. I'll leave you to roll your own code for downsizing to a size which is not a divisor of the original size. And I provide no warranty as to the suitability of this approach for your problem.