From my previous post,
OpenCV : algorithm for simple image rotation and reduction
I still could not rotate my image by an arbitrary number but only 90 degree.
I have changed my code since then, right now is
Mat image1 = imread("Balloon.jpg", CV_LOAD_IMAGE_ANYCOLOR);
Mat rotC(image1.cols, image1.rows, image1.type());
#define PI 3.14156
void rotation(Mat image1)
{
int hwidth = image1.rows / 2;
int hheight = image1.cols / 2;
double angle = 45.00 * PI / 180.0;
for (int x = 0;x < image1.rows;x++) {
for (int y = 0; y < image1.cols;y++) {
int xt = x - hwidth;
int yt = y - hheight;
int xs = (int)round((cos(angle) * xt - sin(angle) * yt) + hwidth);
int ys = (int)round((sin(angle) * xt + cos(angle) * yt) + hheight);
rotC.at<Vec3b>(x,y) = image1.at<Vec3b>(xs, ys);
}
}
}
int main()
{
rotation(image1);
imshow("color", rotC);
waitKey(0);
}
An error came out at rotC.at<Vec3b>(x,y) = image1.at<Vec3b>(xs, ys);
is this the only part where my code have error?
I'm trying to link back my result into the new rotation then display it with imshow.
Please let me know what I'll need to do in order to make it rotate with an arbitrary number such as 30 degree, 46 degree etc.
The result of I wanted to get is shown below, the image is rotated via opencv function.
Thank you!
Related
imageData = new double*[imageHeight];
for(int i = 0; i < imageHeight; i++) {
imageData[i] = new double[imageWidth];
for(int j = 0; j < imageWidth; j++) {
// compute the distance and angle from the swirl center:
double pixelX = (double)i - swirlCenterX;
double pixelY = (double)j - swirlCenterY;
double pixelDistance = pow(pow(pixelX, 2) + pow(pixelY, 2), 0.5);
double pixelAngle = atan2(pixelX, pixelY);
// double swirlAmount = 1.0 - (pixelDistance/swirlRadius);
// if(swirlAmount > 0.0) {
// double twistAngle = swirlTwists * swirlAmount * PI * 2.0;
double twistAngle = swirlTwists * pixelDistance * PI * 2.0;
// adjust the pixel angle and compute the adjusted pixel co-ordinates:
pixelAngle += twistAngle;
pixelX = cos(pixelAngle) * pixelDistance;
pixelY = sin(pixelAngle) * pixelDistance;
// }
(this)->setPixel(i, j, tempMatrix[(int)(swirlCenterX + pixelX)][(int)(swirlCenterY + pixelY)]);
}
}
I am trying to implement a c++ function (code above) based on the following pseudo-code
which is supposed to create a swirl on an image, but I have some continuity problems on the borders.
The function I have for the moment is able to apply the swirl on a disk of a given size and to deform it almost as I whished but its influence doesn't decrease as we get close to the borders. I tried to multiply the angle of rotation by a 1 - (r/R) factor (with r the distance between the current pixel in the function and the center of the swirl, and R the radius of the swirl), but this doesn't give the effect I hoped for.
Moreover, I noticed that at some parts of the border, a thin white line appears (which means that the values of the pixels there is equal to 1) and I can't exactly explain why.
Maybe some of the problems I have are linked to the atan2 C++ standard function.
I am a beginner in c++ and have coded a for loop to show a hollow circle when I run the code, however, I was wondering how I could achieve a filled-in circle using the distance formula (d = sqrt((ax-bx)^2 + (ay-by)^2). Here's what I have so far! Any help would be appreciated!
int MAX = 728;
for (float t = 0; t < 2 * 3.14; t += 0.01)
SetPixel(MAX / 4 + MAX / 6 * sin(t), MAX / 4 + MAX / 6 * cos(t), 255, 255, 0);
#include <windows.h>
#include <iostream>
using namespace std;
int main()
{
HWND consoleWindow = GetConsoleWindow(); // Get a console handle
HDC consoleDC = GetDC(consoleWindow); // Get a handle to device context
int max = 628;
float i = 0;
float t;
float doublePi = 6.29;
for (i = 0.0; i < max; i += 2.0) {
for (t = 0.0; t < doublePi; t += 0.01) {
SetPixel(consoleDC, max / 4 + (max - i) / 6 * sin(t), max / 4 + (max - i) / 6 * cos(t), RGB(255, 255, 0));
}
}
ReleaseDC(consoleWindow, consoleDC);
cin.ignore();
return 0;
}
Working almost well. Draw and fill in! A little slow...
Pffff... do not use sin and cos! instead use the sqrt(1-x^2) approach. You can view the formula rendering a circle in google for example: https://www.google.com/search?q=sqrt(1-x^2)
I edit this answer because it seems that is not clear:
float radius = 50.0f;
for (int x = -radius; x <= radius; ++x) {
int d = round(sqrt(1.0f - (x * x / radius / radius)) * radius);
for (int y = -d; y <= d; ++y) {
SetPixel(x, y, 255, 255, 0);
}
}
Note: each graphic library is different, so I assumed that you used rightfully the "SetPixel" function.
Now, for most people say the sqrt(1-x^2) approach should be enough, but it seem that some downvoters does not think the same XD.
Inefficient as can be, and probably the last way you really want to draw a circle ... but ...
Over the entire square encompassing your circle, calculate each pixel's distance from the center and set if under or equal the radius.
// Draw a circle centered at (Xcenter,Ycenter) with given radius using distance formula
void drawCircle(HDC dc, int XCenter, int YCenter, int radius, COLORREF c) {
double fRad = radius * 1.0; // Just a shortcut to avoid thrashing data types
for (int x = XCenter - radius; x<XCenter + radius; x++) {
for (int y = YCenter - radius; y<YCenter + radius; y++) {
double d = sqrt(((x - XCenter) * (x - XCenter)) + ((y - YCenter) * (y - YCenter)) );
if (d <= fRad) SetPixel(dc, x, y, c);
}
}
}
Caveat: No more caveat, used a C++ environment and tested it this time. :-)
Call thusly:
int main()
{
HWND consoleWindow = GetConsoleWindow();
HDC consoleDC = GetDC(consoleWindow);
drawCircle(consoleDC, 50, 50, 20, RGB(255, 0, 255));
ReleaseDC(consoleWindow, consoleDC);
return 0;
}
Issue
I'm trying to implement the Perlin Noise algorithm in 2D with a single octave with a size of 16x16. I'm using this as heightmap data for a terrain, however it only seems to work in one axis. Whenever the sample point moves to a new Y section in the Perlin Noise grid, the gradient is very different from what I expect (for example, it often flips from 0.98 to -0.97, which is a very sudden change).
This image shows the staggered terrain in the z direction (which is the y axis in the 2D Perlin Noise grid)
Code
I've put the code that calculates which sample point to use at the end since it's quite long and I believe it's not where the issue is, but essentially I scale down the terrain to match the Perlin Noise grid (16x16) and then sample through all the points.
Gradient At Point
So the code that calculates out the gradient at a sample point is the following:
// Find the gradient at a certain sample point
float PerlinNoise::gradientAt(Vector2 point)
{
// Decimal part of float
float relativeX = point.x - (int)point.x;
float relativeY = point.y - (int)point.y;
Vector2 relativePoint = Vector2(relativeX, relativeY);
vector<float> weights(4);
// Find the weights of the 4 surrounding points
weights = surroundingWeights(point);
float fadeX = fadeFunction(relativePoint.x);
float fadeY = fadeFunction(relativePoint.y);
float lerpA = MathUtils::lerp(weights[0], weights[1], fadeX);
float lerpB = MathUtils::lerp(weights[2], weights[3], fadeX);
float lerpC = MathUtils::lerp(lerpA, lerpB, fadeY);
return lerpC;
}
Surrounding Weights of Point
I believe the issue is somewhere here, in the function that calculates the weights for the 4 surrounding points of a sample point, but I can't seem to figure out what is wrong since all the values seem sensible in the function when stepping through it.
// Find the surrounding weight of a point
vector<float> PerlinNoise::surroundingWeights(Vector2 point){
// Produces correct values
vector<Vector2> surroundingPoints = surroundingPointsOf(point);
vector<float> weights;
for (unsigned i = 0; i < surroundingPoints.size(); ++i) {
// The corner to the sample point
Vector2 cornerToPoint = surroundingPoints[i].toVector(point);
// Getting the seeded vector from the grid
float x = surroundingPoints[i].x;
float y = surroundingPoints[i].y;
Vector2 seededVector = baseGrid[x][y];
// Dot product between the seededVector and corner to the sample point vector
float dotProduct = cornerToPoint.dot(seededVector);
weights.push_back(dotProduct);
}
return weights;
}
OpenGL Setup and Sample Point
Setting up the heightmap and getting the sample point. Variables 'wrongA' and 'wrongA' is an example of when the gradient flips and changes suddenly.
void HeightMap::GenerateRandomTerrain() {
int perlinGridSize = 16;
PerlinNoise perlin_noise = PerlinNoise(perlinGridSize, perlinGridSize);
numVertices = RAW_WIDTH * RAW_HEIGHT;
numIndices = (RAW_WIDTH - 1) * (RAW_HEIGHT - 1) * 6;
vertices = new Vector3[numVertices];
textureCoords = new Vector2[numVertices];
indices = new GLuint[numIndices];
float perlinScale = RAW_HEIGHT/ (float) (perlinGridSize -1);
float height = 50;
float wrongA = perlin_noise.gradientAt(Vector2(0, 68.0f / perlinScale));
float wrongB = perlin_noise.gradientAt(Vector2(0, 69.0f / perlinScale));
for (int x = 0; x < RAW_WIDTH; ++x) {
for (int z = 0; z < RAW_HEIGHT; ++z) {
int offset = (x* RAW_WIDTH) + z;
float xVal = (float)x / perlinScale;
float yVal = (float)z / perlinScale;
float noise = perlin_noise.gradientAt(Vector2( xVal , yVal));
vertices[offset] = Vector3(x * HEIGHTMAP_X, noise * height, z * HEIGHTMAP_Z);
textureCoords[offset] = Vector2(x * HEIGHTMAP_TEX_X, z * HEIGHTMAP_TEX_Z);
}
}
numIndices = 0;
for (int x = 0; x < RAW_WIDTH - 1; ++x) {
for (int z = 0; z < RAW_HEIGHT - 1; ++z) {
int a = (x * (RAW_WIDTH)) + z;
int b = ((x + 1)* (RAW_WIDTH)) + z;
int c = ((x + 1)* (RAW_WIDTH)) + (z + 1);
int d = (x * (RAW_WIDTH)) + (z + 1);
indices[numIndices++] = c;
indices[numIndices++] = b;
indices[numIndices++] = a;
indices[numIndices++] = a;
indices[numIndices++] = d;
indices[numIndices++] = c;
}
}
BufferData();
}
Turned out the issue was in the interpolation stage:
float lerpA = MathUtils::lerp(weights[0], weights[1], fadeX);
float lerpB = MathUtils::lerp(weights[2], weights[3], fadeX);
float lerpC = MathUtils::lerp(lerpA, lerpB, fadeY);
I had the interpolation in the y axis the wrong way around, so it should have been:
lerp(lerpB, lerpA, fadeY)
Instead of:
lerp(lerpA, lerpB, fadeY)
I’m using a modified version of a gauss-newton method to refine a pose estimate using OpenCV. The unmodified code can be found here: http://people.rennes.inria.fr/Eric.Marchand/pose-estimation/tutorial-pose-gauss-newton-opencv.html
The details of this approach are outlined in the corresponding paper:
Marchand, Eric, Hideaki Uchiyama, and Fabien Spindler. "Pose
estimation for augmented reality: a hands-on survey." IEEE
transactions on visualization and computer graphics 22.12 (2016):
2633-2651.
A PDF can be found here: https://hal.inria.fr/hal-01246370/document
The part that is relevant (Pages 4 and 5) are screencapped below:
Here is what I have done. First, I’ve (hopefully) “corrected” some errors: (a) dt and dR can be passed by reference to exponential_map() (even though cv::Mat is essentially a pointer). (b) The last entry of each 2x6 Jacobian matrix, J.at<double>(i*2+1,5), was -x[i].y but should be -x[i].x. (c) I’ve also tried using a different formula for the projection. Specifically, one that includes the focal length and principal point:
xq.at<double>(i*2,0) = cx + fx * cX.at<double>(0,0) / cX.at<double>(2,0);
xq.at<double>(i*2+1,0) = cy + fy * cX.at<double>(1,0) / cX.at<double>(2,0);
Here is the relevant code I am using, in its entirety (control starts at optimizePose3()):
void exponential_map(const cv::Mat &v, cv::Mat &dt, cv::Mat &dR)
{
double vx = v.at<double>(0,0);
double vy = v.at<double>(1,0);
double vz = v.at<double>(2,0);
double vtux = v.at<double>(3,0);
double vtuy = v.at<double>(4,0);
double vtuz = v.at<double>(5,0);
cv::Mat tu = (cv::Mat_<double>(3,1) << vtux, vtuy, vtuz); // theta u
cv::Rodrigues(tu, dR);
double theta = sqrt(tu.dot(tu));
double sinc = (fabs(theta) < 1.0e-8) ? 1.0 : sin(theta) / theta;
double mcosc = (fabs(theta) < 2.5e-4) ? 0.5 : (1.-cos(theta)) / theta / theta;
double msinc = (fabs(theta) < 2.5e-4) ? (1./6.) : (1.-sin(theta)/theta) / theta / theta;
dt.at<double>(0,0) = vx*(sinc + vtux*vtux*msinc)
+ vy*(vtux*vtuy*msinc - vtuz*mcosc)
+ vz*(vtux*vtuz*msinc + vtuy*mcosc);
dt.at<double>(1,0) = vx*(vtux*vtuy*msinc + vtuz*mcosc)
+ vy*(sinc + vtuy*vtuy*msinc)
+ vz*(vtuy*vtuz*msinc - vtux*mcosc);
dt.at<double>(2,0) = vx*(vtux*vtuz*msinc - vtuy*mcosc)
+ vy*(vtuy*vtuz*msinc + vtux*mcosc)
+ vz*(sinc + vtuz*vtuz*msinc);
}
void optimizePose3(const PoseEstimation &pose,
std::vector<FeatureMatch> &feature_matches,
PoseEstimation &optimized_pose) {
//Set camera parameters
double fx = camera_matrix.at<double>(0, 0); //Focal length
double fy = camera_matrix.at<double>(1, 1);
double cx = camera_matrix.at<double>(0, 2); //Principal point
double cy = camera_matrix.at<double>(1, 2);
auto inlier_matches = getInliers(pose, feature_matches);
std::vector<cv::Point3d> wX;
std::vector<cv::Point2d> x;
const unsigned int npoints = inlier_matches.size();
cv::Mat J(2*npoints, 6, CV_64F);
double lambda = 0.25;
cv::Mat xq(npoints*2, 1, CV_64F);
cv::Mat xn(npoints*2, 1, CV_64F);
double residual=0, residual_prev;
cv::Mat Jp;
for(auto i = 0u; i < npoints; i++) {
//Model points
const cv::Point2d &M = inlier_matches[i].model_point();
wX.emplace_back(M.x, M.y, 0.0);
//Imaged points
const cv::Point2d &I = inlier_matches[i].image_point();
xn.at<double>(i*2,0) = I.x; // x
xn.at<double>(i*2+1,0) = I.y; // y
x.push_back(I);
}
//Initial estimation
cv::Mat cRw = pose.rotation_matrix;
cv::Mat ctw = pose.translation_vector;
int nIters = 0;
// Iterative Gauss-Newton minimization loop
do {
for (auto i = 0u; i < npoints; i++) {
cv::Mat cX = cRw * cv::Mat(wX[i]) + ctw; // Update cX, cY, cZ
// Update x(q)
//xq.at<double>(i*2,0) = cX.at<double>(0,0) / cX.at<double>(2,0); // x(q) = cX/cZ
//xq.at<double>(i*2+1,0) = cX.at<double>(1,0) / cX.at<double>(2,0); // y(q) = cY/cZ
xq.at<double>(i*2,0) = cx + fx * cX.at<double>(0,0) / cX.at<double>(2,0);
xq.at<double>(i*2+1,0) = cy + fy * cX.at<double>(1,0) / cX.at<double>(2,0);
// Update J using equation (11)
J.at<double>(i*2,0) = -1 / cX.at<double>(2,0); // -1/cZ
J.at<double>(i*2,1) = 0;
J.at<double>(i*2,2) = x[i].x / cX.at<double>(2,0); // x/cZ
J.at<double>(i*2,3) = x[i].x * x[i].y; // xy
J.at<double>(i*2,4) = -(1 + x[i].x * x[i].x); // -(1+x^2)
J.at<double>(i*2,5) = x[i].y; // y
J.at<double>(i*2+1,0) = 0;
J.at<double>(i*2+1,1) = -1 / cX.at<double>(2,0); // -1/cZ
J.at<double>(i*2+1,2) = x[i].y / cX.at<double>(2,0); // y/cZ
J.at<double>(i*2+1,3) = 1 + x[i].y * x[i].y; // 1+y^2
J.at<double>(i*2+1,4) = -x[i].x * x[i].y; // -xy
J.at<double>(i*2+1,5) = -x[i].x; // -x
}
cv::Mat e_q = xq - xn; // Equation (7)
cv::Mat Jp = J.inv(cv::DECOMP_SVD); // Compute pseudo inverse of the Jacobian
cv::Mat dq = -lambda * Jp * e_q; // Equation (10)
cv::Mat dctw(3, 1, CV_64F), dcRw(3, 3, CV_64F);
exponential_map(dq, dctw, dcRw);
cRw = dcRw.t() * cRw; // Update the pose
ctw = dcRw.t() * (ctw - dctw);
residual_prev = residual; // Memorize previous residual
residual = e_q.dot(e_q); // Compute the actual residual
std::cout << "residual_prev: " << residual_prev << std::endl;
std::cout << "residual: " << residual << std::endl << std::endl;
nIters++;
} while (fabs(residual - residual_prev) > 0);
//} while (nIters < 30);
optimized_pose.rotation_matrix = cRw;
optimized_pose.translation_vector = ctw;
cv::Rodrigues(optimized_pose.rotation_matrix, optimized_pose.rotation_vector);
}
Even when I use the functions as given, it does not produce the correct results. My initial pose estimate is very close to optimal, but when I try run the program, the method takes a very long time to converge - and when it does, the results are very wrong. I’m not sure what could be wrong and I’m out of ideas. I’m confident my inliers are actually inliers (they were chosen using an M-estimator). I’ve compared the results from exponential map with those from other implementations, and they seem to agree.
So, where is the error in this gauss-newton implementation for pose optimization? I’ve tried to make things as easy as possible for anyone willing to lend a hand. Let me know if there is anymore information I can provide. Any help would be greatly appreciated. Thanks.
Edit: 2019/05/13
There is now solvePnPRefineVVS function in OpenCV.
Also, you should use x and y calculated from the current estimated pose instead.
In the cited paper, they expressed the measurements x in the normalized camera frame (at z=1).
When working with real data, you have:
(u,v): 2D image coordinates (e.g. keypoints, corner locations, etc.)
K: the intrinsic parameters (obtained after calibrating the camera)
D: the distortion coefficients (obtained after calibrating the camera)
To compute the 2D image coordinates in the normalized camera frame, you can use in OpenCV the function cv::undistortPoints() (link to my answer about cv::projectPoints() and cv::undistortPoints()).
When there is no distortion, the computation (also called "reverse perspective transformation") is:
x = (u - cx) / fx
y = (v - cy) / fy
I have tried image rotation and reduction(JPEG) with getRotationMatrix2D(center, angle, scale);
and warpAffine(image1, image3, rotation, image3.size());
I got the result I wanted(image as below)
for (int r = 0;r < image1.rows;r++) {
for (int c = r + 1;c < image1.cols;c++) {
Point center = Point(image1.cols / 2, image1.rows / 2);
Point center1 = Point(image1.cols / 2, image1.rows / 2);
double angle = 90.0;
double scale = 1;
double angle1 = 90.0;
double scale1 = 0.5;
rotation = getRotationMatrix2D(center, angle, scale);
rotation1 = getRotationMatrix2D(center1, angle1, scale1);
but I want to learn some simple rotation and reduction algorithm ( simple for beginner like me ) without using any library
to get the same result.
After searching for various solutions, i ended up with this
from https://gamedev.stackexchange.com/questions/67613/how-can-i-rotate-a-bitmap-without-d3d-or-opengl
Can anyone break up bit by bit of the simple linear algebra to explain to me in regards to my pesudo code?
EDIT:
Reduction code
void reduction(Mat image1)
{
for (int r = 0;r < imgC.rows;r++)
{
for (int c = 0;c < imgC.cols;c++)
{
int new_x = c * (125 / 256);
int new_y = r * (125 / 256);
imgC.at<uchar>(r, c) = imgC.at<uchar>(new_y, new_x);
}
}
}
In this example I have loaded two images. One is in gray scale and other is color image. Both are same image so you can understand easily how to handle rotation with mathematical equation. Please see this example which is very easy to understand. Also in the similar manner you can add scaling and reduction. Here each point is converted according to the equation and color value set on new location. Here is the code:
#include <iostream>
#include <string>
#include "opencv/highgui.h"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/objdetect/objdetect.hpp"
using namespace std;
using namespace cv;
#define PIPI 3.14156
int main()
{
Mat img = imread("C:/Users/dell2/Desktop/DSC00587.JPG",0);//loading gray scale image
Mat imgC = imread("C:/Users/dell2/Desktop/DSC00587.JPG",1);//loading color image
Mat rotC(imgC.cols, imgC.rows, imgC.type());
rotC = Scalar(0,0,0);
Mat rotG(img.cols, img.rows, img.type());
rotG = Scalar(0,0,0);
float angle = 90.0 * PIPI / 180.0;
for(int r=0;r<imgC.rows;r++)
{
for(int c=0;c<imgC.cols;c++)
{
float new_px = c * cos(angle) - r * sin(angle);
float new_py = c * sin(angle) + r * cos(angle);
Point pt((int)-new_px, (int)new_py);
//color image
rotC.at<Vec3b>(pt) = imgC.at<Vec3b>(r,c);//assign color value at new location from original image
//gray scale image
rotG.at<uchar>(pt) = img.at<uchar>(r,c);//assign color value at new location from original image
}
}
imshow("color",rotC);
imshow("gray",rotG);
waitKey(0);
return 0;
}