In my Flutter app, I have drawn a rectangle in one coordinate system on a Google Map and I want to rotate this coordinate system around the coordinate (0,0). How this can be done is described here: Rotation matrix. I have implemented the rotation of coordinates like this in my app:
class Coordinate {
Coordinate(this.x, this.y);
double x;
double y;
void rotate(double angle) {
// angle is in degrees, convert to radians
final double angleInRadians = angle * (math.pi / 180);
final double oldX = x;
final double oldY = y;
final double newX = oldX * math.cos(angleInRadians) - oldY * math.sin(angleInRadians);
final double newY = oldX * math.sin(angleInRadians) + oldY * math.cos(angleInRadians);
x = newX;
y = newY;
}
}
Using this to rotate four corners of a rectangle works fine for some places in the world, like Australia and a place near (0,0). In this picture, you can see the rotation of the red rectangle on the top around the point (0,0) (bottom left) to the rotated red rectangle on the bottom right:
Please ignore all the markers and non-red lines. The next to images show a rectangle like the one in the picture above which I again rotate around (0,0) using the code above, but as you can see in the second image, the red rectangle is not a rectangle anymore, but a parallelogram instead.
I cannot find an explanation for this. The questions I ask myself are: Why does the code work for some places but not for others? Why does the rectangle transform to a perfect parallelogram instead of whatever other shape with the points possibly all around the world? As it is working for some locations, the problem cannot be that I have mixed up some of the coordinates or something like that, it has to do something with the calculation of the edges of the rotated rectangle. But I cannot find out what I did wrong here.
REMINDER: IGNORE ALL MARKERS AND NON-RED LINES
Here is what I want to do.
I need to draw in a QGraphicsView a series of rectangles that are aligned left and right. By this I mean that if rectangle i has posion (0,y) rectangle i+1 needs to have position (0,max) where max is such that the right side of the rectangle "touches" the right side of the QGraphicsView.
When the window is resized I need to recalculate the value of max such that the rectangle is always touching the right side of the screen.
Here is how I add my scene (this references a class that inherits QGraphicsView)
scene = new QGraphicsScene(this);
this->setScene(scene);
this->setAlignment(Qt::AlignTop|Qt::AlignLeft);
To add a rectangle that touches the left border I add it a (0,yvalue,width,height).
How do I calculate the value of X so that that the rectangle will touch the right border?
Ok. So this should go in the resize event of the QGraphicsView. But this does what I wanted:
void AView::resized(){
scene->clear();
QSize newsize = this->size();
qreal w = newsize.width()*.99;
qreal h = newsize.height()*.99;
this->setSceneRect(0,0,w,h);
scene->setSceneRect(0,0,w,h);
qreal rectwidth = 100;
qreal newx = w - rectwidth;
left = new QGraphicsRectItem(0,0,rectwidth,100);
left->setBrush(QBrush(Qt::red));
right = new QGraphicsRectItem(newx,100,rectwidth,100);
right->setBrush(QColor(Qt::blue));
scene->addItem(left);
scene->addItem(right);
}
In this way the blue rectangle is pretty much always at the border. Aslo there is no stretching of the image. There is a gap between the right borders which increases due to the window resizing, but it seems that the size returned by this->size() is slightly larger than the "white area" that you see on screen. Adding the .99 gives a much better results from my experiments.
This little example should calculate the shift and move all items from a selection or list of items, based on alignment you desire (I showed right, but let, center, top, bottom would be the same).
QRectF refRect = scene()->sceneRect();
QList<QGraphicsItem*> sel = allItemsYouWantAligned; // scene()->selectedItems(); for example
foreach(QGraphicsItem* selItem, sel)
{
qreal dx = 0, dy = 0;
QRectF itemRect = selItem->mapToScene(selItem->boundingRect()).boundingRect();
if(align_right)
dx = refRect.right() - itemRect.right();
else
.. calculate either dx or dy on how you want to align
selItem->moveBy(dx, dy);
}
Re-reading the question - I see you don't really need to move rectangle, but create new larger and larger rectangles.
Your solution is simple enough. If you want to resize instead of move -
you would have to setRect() on your items by dx increment:
QRectF r = selItem->rect();
r.setWidth(r.width + dx);
selItem->setRect(r);
I'm trying to draw linear gradients with libpixman using the pixman_image_create_linear_gradient() function. It works fine for drawing gradients that run from left to right and from top to bottom but I don't see how I can draw gradients at a specific angle (0-360 degrees) like they're possible in CSS. For example, a linear gradient that is rotated by 45 degrees.
I think one has to use the arguments p1 and p2 for this because they define the gradient direction but there is no documentation at all and I can't really figure out how to use these two parameters to rotate the gradient.
For vertical gradients I simply set them to
p1.x = 0; p1.y = 0;
p2.x = 0; p2.y = height - 1;
And for horizontal gradients I use
p1.x = 0; p1.y = 0;
p2.x = width - 1; p2.y = 0;
But which values should I use for arbitrary rotation? Simply applying a 2D rotation matrix to the points doesn't look right, e.g. when drawing a gradient that is 640x480 and rotating this by 45 degrees I end up with the points
p1.x = 81; p1.y = 560;
p2.x = 559; p2.y = 559;
which draws the gradient in the right direction but there are about 80 pixels of blank space on either side of the gradient so I must be doing something wrong.
Could anybody tell me how to get this right?
Thanks!
I guess that Pixman implements linear gradients in the same way that Cairo does, given that Cairo's image backend uses Pixman, so look at some docs for Cairo. For example, in http://www.cairographics.org/tutorial/ in the section "Drawing with Cairo", subsection "Preparing and Selecting a Source" there is an explanation of linear gradients.
For your 45 degree rotation, I would try the following (one point in the top left corner, the other one in the bottom right one):
p1.x = 0; p1.y = 0;
p2.x = width - 1; p2.y = height - 1;
P.S.: No, I do not know how gradients with an angle are specified in CSS.
I am currently studying a module in computer vision called edge detection.
I am trying to understand the meaning of gradient orientation and gradient magnitude.
As explained by Dima in his answer, you should be familiar with the mathematical concept of gradient in order to better understand the gradient in the field of image processing.
My answer is based on the answer of mevatron to this question.
Here you find a simple initial image of a white disk on a black background:
you can compute an approximation of the gradient of this image. As Dima explained in his answer, you have two component of the gradient, an horizontal and a vertical component.
The following images shows you the horizontal component:
it shows how much the gray levels in your image change in the horizontal direction (it is the direction of positive x, scanning the image from left to right), this change is "encoded" in the grey level of the image of the horizontal component: the mean grey level means no change, the bright levels mean change from a dark value to a bright value, the dark levels mean a change from a bright value to a dark value. So, in the above image you see the brighter value in the left part of the circle because it is in the left part of the initial image that you have the black to white transition that gives you the left edge of the disk; similarly, in the above image you see the darker value in the right part of the circle because it is in the right part of the initial image that you have the white to black transition that gives you the right edge of the disk. In the above image, the inner part of the disk and the background are at a mean grey level because there is no change inside the disk and in the background.
We can make analogous observations for the vertical component, it shows how the image change in the vertical direction, i.e. scanning the image from the top to the bottom:
You can now combine the two components in order to get the magnitude of the gradient and the orientation of the gradient.
The following image is the magnitude of the gradient:
Again, in the above image the change in initial image is encoded in the gray level: here you see that white means an high change in the initial image while black means no change at all.
So, when you look at the image of the magnitude of the gradient you can say "if the image is bright it means a big change in the initial image; if it is dark it means no change or very llittle change".
The following image is the orientation of the gradient:
In the above image the orientation is again encoded as gray levels: you can think at the orientation as the angle of an arrow pointing from the the dark part of the image to the bright part of the image; the angle is referred to an xy frame where the x runs from left to right while the y runs from top to bottom. In the above image you see all the grey level from the black (zero degree) to the white (360 degree). We can encode the information with color:
in the above image the information is encode in this way:
red: the angle is between 0 and 90 degree
cyan: the angle is between 90 and 180 degree
green: the angle is between 180 and 270 degree
yellow: the angle is between 270 and 360 degree
Here it is the C++ OpenCV code for producing the above images.
Pay attention to the fact that, for the computation of the orientation, I use the function cv::phase which, as explained in the doc, gives an angle of 0 when both the vertical component and the horizontal component of the gradient are zero; that may be convenient but from a mathematical point of view is plainly wrong because when both components are zero the orientation is not defined and the only meaningful value for an orientation kept in a floating-point C++ type is a NaN.
It is plainly wrong because a 0 degree orientation, for example, is already related to an horizontal edge and it cannot be used to represent something else like a region with no edges and so a region where orientation is meaningless.
// original code by https://stackoverflow.com/users/951860/mevatron
// see https://stackoverflow.com/a/11157426/15485
// https://stackoverflow.com/users/15485/uvts-cvs added the code for saving x and y gradient component
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
#include <vector>
using namespace cv;
using namespace std;
Mat mat2gray(const cv::Mat& src)
{
Mat dst;
normalize(src, dst, 0.0, 255.0, cv::NORM_MINMAX, CV_8U);
return dst;
}
Mat orientationMap(const cv::Mat& mag, const cv::Mat& ori, double thresh = 1.0)
{
Mat oriMap = Mat::zeros(ori.size(), CV_8UC3);
Vec3b red(0, 0, 255);
Vec3b cyan(255, 255, 0);
Vec3b green(0, 255, 0);
Vec3b yellow(0, 255, 255);
for(int i = 0; i < mag.rows*mag.cols; i++)
{
float* magPixel = reinterpret_cast<float*>(mag.data + i*sizeof(float));
if(*magPixel > thresh)
{
float* oriPixel = reinterpret_cast<float*>(ori.data + i*sizeof(float));
Vec3b* mapPixel = reinterpret_cast<Vec3b*>(oriMap.data + i*3*sizeof(char));
if(*oriPixel < 90.0)
*mapPixel = red;
else if(*oriPixel >= 90.0 && *oriPixel < 180.0)
*mapPixel = cyan;
else if(*oriPixel >= 180.0 && *oriPixel < 270.0)
*mapPixel = green;
else if(*oriPixel >= 270.0 && *oriPixel < 360.0)
*mapPixel = yellow;
}
}
return oriMap;
}
int main(int argc, char* argv[])
{
Mat image = Mat::zeros(Size(320, 240), CV_8UC1);
circle(image, Point(160, 120), 80, Scalar(255, 255, 255), -1, CV_AA);
imshow("original", image);
Mat Sx;
Sobel(image, Sx, CV_32F, 1, 0, 3);
Mat Sy;
Sobel(image, Sy, CV_32F, 0, 1, 3);
Mat mag, ori;
magnitude(Sx, Sy, mag);
phase(Sx, Sy, ori, true);
Mat oriMap = orientationMap(mag, ori, 1.0);
imshow("x", mat2gray(Sx));
imshow("y", mat2gray(Sy));
imwrite("hor.png",mat2gray(Sx));
imwrite("ver.png",mat2gray(Sy));
imshow("magnitude", mat2gray(mag));
imshow("orientation", mat2gray(ori));
imshow("orientation map", oriMap);
waitKey();
return 0;
}
The gradient of a function of two variables x, y is a vector of the partial derivatives in the x and y direction. So if your function is f(x,y), the gradient is the vector (f_x, f_y). An image is a discrete function of (x,y), so you can also talk about the gradient of an image.
The gradient of the image has two components: the x-derivative and the y-derivative. So, you can think of it as vectors (f_x, f_y) defined at each pixel. These vectors have a direction atan(f_y / fx) and a magnitude sqrt(f_x^2 + f_y^2). So, you can represent the gradient of an image either an x-derivative image and a y-derivative image, or as direction image and a magnitude image.
I'm trying to align 4 different parallax backgrounds in my game. These aren't endless tiling backgrounds like in a space shooter, they don't fill the whole screen. They're a row of trees running along the ground.
My images are bottom left aligned and I'm trying to align them all to the lowest point on the terrain.
I have the following information: The lowest point on the map that I want everything to line up at (is 300). The parallax / scroll factor of the background. The height of the background.
I've tried:
background.y = lowestPoint; // doesn't work
background.y = lowestPoint * parallaxFactor; // doesn't work, way off
background.y = lowestPoint + lowestPoint * parallaxFactor; // doesn't work
I'm obviously missing something here.
Basically, I'm trying to calculate where to put the background tile's registration point based on it's parallax factor and lowest point in the terrain.
Ideas?
If I understand this correctly; Screen position is calculated as
screenX = (positionX - cameraX) * parallaxFactor
screenY = (positionY - cameraY) * parallaxFactor
Then to align two positions with different parallax factors at some camera position, you need to match screen coordinates:
obj1.screenY = (obj1.positionY - cameraY) * obj1.parallaxFactor
obj2.screenY = (obj2.positionY - cameraY) * obj2.parallaxFactor
(obj1.positionY - cameraY) * obj1.parallaxFactor =
(obj2.positionY - cameraY) * obj2.parallaxFactor
obj1.positionY =
(obj2.positionY - cameraY) * F + cameraY
where F = obj2.parallaxFactor / obj1.parallaxFactor.
Similarly for the horizontal direction.