I am trying to find the simplest way to rotate and display a TBitmap on its center by any given angle needed. The TBitmap is square and any clipping that might occur is not important so long as the rotated bitmap's center point remains constant. The image is very small, only around 50 x 50 pixels so speed isn't an issue. Here is the code I have so far which rotates a TBitmap to 90 degrees, which is simple, the any angle thing less so.
std::auto_ptr<Graphics::TBitmap> bitmap1(new Graphics::TBitmap);
std::auto_ptr<Graphics::TBitmap> bitmap2(new Graphics::TBitmap);
bitmap1->LoadFromFile("c:/myimage.bmp");
bitmap1->Transparent = true;
bitmap1->TransparentColor = bitmap1->Canvas->Pixels[50][50];
bitmap2->Width=bitmap1->Height;
bitmap2->Height=bitmap1->Width;
double x1 = 0.0;
double y1 = 0.0;
for (int x = 0;x < bitmap1->Width; x++)
{
for(int y = 0;y < bitmap1->Height;y++)
{
x1 = std::cos(45.0) * x - std::sin(45.0) * y;
y1 = sin(45.0) * x + cos(45.0) * y;
bitmap2->Canvas->Pixels[x1][y1] =
bitmap1->Canvas->Pixels[x][y];
}
}
Form1->Canvas->Draw( 500, 200, bitmap2.get());
See revised code... This allows for rotation but the copy creates a hazy image and the rotation point is at the top left.
you are doing this the other way around so there may be present holes in the resulting image because you are looping the source pixels with 1 pixel step .... to remedy this loop the target pixels instead...
loop through bitmap2 pixels (x2,y2)
for each compute rotated-back (x1,y1) position in bitmap1
copy pixel value
if (x1,y1) is outside bitmap1 then use backgroun color like clBlack instead.
To improve speed use TBitmap->ScanLine[y] property that will improve speed at least 1000x times if used right see:
Display an array of color in C
After I put all this together I got this:
#include <math.h> // just for cos,sin
// rotate src around x0,y0 [pixels] by angle [rad] and store result in dst
void rotate(Graphics::TBitmap *dst,Graphics::TBitmap *src,double x0,double y0,double angle)
{
int x,y,xx,yy,xs,ys;
double s,c,fx,fy;
// resize dst to the same size as src
xs=src->Width;
ys=src->Height;
dst->SetSize(xs,ys);
// allow direct pixel access for src
src->HandleType=bmDIB;
src->PixelFormat=pf32bit;
DWORD **psrc=new DWORD*[ys];
for (y=0;y<ys;y++) psrc[y]=(DWORD*)src->ScanLine[y];
// allow direct pixel access for dst
dst->HandleType=bmDIB;
dst->PixelFormat=pf32bit;
DWORD **pdst=new DWORD*[ys];
for (y=0;y<ys;y++) pdst[y]=(DWORD*)dst->ScanLine[y];
// precompute variables
c=cos(angle);
s=sin(angle);
// loop all dst pixels
for (y=0;y<ys;y++)
for (x=0;x<xs;x++)
{
// compute position in src
fx=x; // convert to double
fy=y;
fx-=x0; // translate to center of rotation
fy-=y0;
xx=double(+(fx*c)+(fy*s)+x0); // rotate and translate back
yy=double(-(fx*s)+(fy*c)+y0);
// copy pixels
if ((xx>=0)&&(xx<xs)&&(yy>=0)&&(yy<ys)) pdst[y][x]=psrc[yy][xx];
else pdst[y][x]=0; // black
}
// free memory
delete[] psrc;
delete[] pdst;
}
usage:
// init
Graphics::TBitmap *bmp1,*bmp2;
bmp1=new Graphics::TBitmap;
bmp1->LoadFromFile("image.bmp");
bmp1->HandleType=bmDIB;
bmp1->PixelFormat=pf32bit;
bmp2=new Graphics::TBitmap;
bmp2->HandleType=bmDIB;
bmp2->PixelFormat=pf32bit;
// rotate
rotate(bmp2,bmp1,bmp1->Width/2,bmp1->Height/2,25.0*M_PI/180.0);
// here render bmp2 or whatever
// exit
delete bmp1;
delete bmp2;
Here example output:
On the left is bmp1 and on the right rotated bmp2
Related
I am trying to implement a custom painter that can draw an image (scaled down version) on the canvas and the drawn image can be rotated and scaled.
I get to know that to scale the image I have to scale the canvas using scale method.
Now the questions is how to rotate the scaled image on its center (or any other point). The rotate method of canvas allow only to rotate on top left corner.
Here is my implementation that can be extended
Had the same problem, Solution was simply making your own rotation method in three lines
void rotate(Canvas canvas, double cx, double cy, double angle) {
canvas.translate(cx, cy);
canvas.rotate(angle);
canvas.translate(-cx, -cy);
}
We thus first move the canvas towards the point you want to pivot around. We then rotate along the the topleft (default for Flutter) which in coordinate space is the pivot you want and then put the canvas back to the desired position, with the rotation applied. Method is very efficient, requiring only 4 additions for the translation and the rotation cost is identical to the original one.
This can achieve by shifting the coordinate space as illustrated in figure 1.
The translation is the difference in coordinates between C1 and C2, which are exactly as between A and B in figure 2.
With some geometry formulas, we can calculate the desired translation and produce the rotated image as in the method below
ui.Image rotatedImage({ui.Image image, double angle}) {
var pictureRecorder = ui.PictureRecorder();
Canvas canvas = Canvas(pictureRecorder);
final double r = sqrt(image.width * image.width + image.height * image.height) / 2;
final alpha = atan(image.height / image.width);
final beta = alpha + angle;
final shiftY = r * sin(beta);
final shiftX = r * cos(beta);
final translateX = image.width / 2 - shiftX;
final translateY = image.height / 2 - shiftY;
canvas.translate(translateX, translateY);
canvas.rotate(angle);
canvas.drawImage(image, Offset.zero, Paint());
return pictureRecorder.endRecording().toImage(image.width, image.height);
}
alpha, beta, angle are all in radian.
Here is the repo of the demo app
If you don't want to rotate the image around the center of the image you can use this way. You won't have to care about what the offset of the canvas should be in relation to the image rotation, because the canvas is moved back to its original position after the image is drawn.
void rotate(Canvas c, Image image, Offset focalPoint, Size screenSize, double angle) {
c.save();
c.translate(screenSize.width/2, screenSize.height/2);
c.rotate(angle);
// To rotate around the center of the image, focal point is the
// image width and height divided by 2
c.drawImage(image, focalPoint*-1, Paint());
c.translate(-screenSize.width/2, -screenSize.height/2);
c.restore();
}
Recently I had the idea to make a pendulum out of points using Processing, and with a little learning I solved it easily:
int contador = 0;
int curvatura = 2;
float pendulo;
void setup(){
size(300,300);
}
void draw(){
background(100);
contador = (contador + 1) % 360; //"CONTADOR" GOES FROM 0 TO 359
pendulo = sin(radians(contador))*curvatura; //"PENDULO" EQUALS THE SIN OF CONTADOR, SO IT GOES FROM 1 TO -1 REPEATEDLY, THEN IS MULTIPLIED TO EMPHASIZE OR REDUCE THE CURVATURE OF THE LINE.
tallo(width/2,height/3);
println(pendulo);
}
void tallo (int x, int y){ //THE FUNTION TO DRAW THE DOTTED LINE
pushMatrix();
translate(x,y);
float _y = 0.0;
for(int i = 0; i < 25; i++){ //CREATES THE POINTS SEQUENCE.
ellipse(0,0,5,5);
_y+=5;
rotate(radians(pendulo)); //ROTATE THEM ON EACH ITERATION, THIS MAKES THE SPIRAL.
}
popMatrix();
}
So, in a brief, what I did was a function that changed every point position with the rotate fuction, and then I just had to draw the ellipses in the origin coordinates as that is the real thing that changes position and creates the pendulum ilussion.
[capture example, I just need 2 more points if you are so gentile :)]
[capture example]
[capture example]
Everything was OK that far. The problem appeared when I tried to replace the ellipses for a path made of vertices. The problem is obvious: the path is never (visually) made because all vertices would be 0,0 as they move along with the zero coordinates.
So, in order to make the path possible, I need the absolute values for each vertex; and there's the question: How do I get them?
What I know I have to do is to remove the transform functions, create the variables for the X and Y position and update them inside the for, but then what? That's why I cleared this is a maths issue, which operation I have to add in the X and Y variables in order to make the path and its curvature possible?
void tallo (int x, int y){
pushMatrix();
translate(x,y);
//NOW WE START WITH THE CHANGES. LET'S DECLARE THE VARIABLES FOR THE COORDINATES
float _x = 0.0;
float _y = 0.0;
beginShape();
for(int i = 0; i < 25; i++){ //CREATES THE DOTS.
vertex(_x,_y); //CHANGING TO VERTICES AND CALLING THE NEW VARIABLES, OK.
//rotate(radians(pendulo)); <--- HERE IS MY PROBLEM. HOW DO I CONVERT THIS INTO X AND Y COORDINATES?
//_x = _x + ????;
_y = _y + 5 /* + ???? */;
}
endShape();
popMatrix();
}
We need to have in mind that pendulo's x and y values changes in each iteration of the for, it doesn't has to add the same quantity each time. The addition must be progressive. Otherwise, we would see a straight line rotating instead of a curve accentuating its curvature (if you increase curvatura's value to a number greater than 20, you will notice the spiral)
So, rotating the coordinates was a great solution to it, now it's kind of a muddle to think the mathematical solution to the x and y coordinates for the spiral, my secondary's knowledges aren't enough. I know I have to create another variable inside the for in order to do this progression, but what operation should it have?
I would be really glad to know, maths
You could use simple trigonometry. You know the angle and the hypotenuse, so you use cos to get the relative x position, and sin to the y. The position would be relative to the central point.
But before i explain in detail and draw some explanations, let me propose another solution: PVectors
void setup() {
size(400,400);
frameRate(60);
center = new PVector(width/2, height/3); //defined here because width and height only are set after size()
}
void draw() {
background(255);
fill(0);
stroke(0);
angle = arc_magn*sin( (float) frameCount/60 );
draw_pendulum( center );
}
PVector center;
float angle = 0;
float arc_magn = HALF_PI;
float wire_length = 150;
float rotation_angle = PI/20 /60 ; //we divide it by 60 so the first part is the rotation in one second
void draw_pendulum(PVector origin){
PVector temp_vect = PVector.fromAngle( angle + HALF_PI);
temp_vect.setMag(wire_length);
PVector final_pos = new PVector(origin.x+temp_vect.x, origin.y+temp_vect.y );
ellipse( final_pos.x, final_pos.y, 40, 40);
line(origin.x, origin.y, final_pos.x, final_pos.y);
}
You use PVector class static method fromAngle( float angle ) that returns a unity vector of the given angle, then use .setMag() to define it's length.
Those PVector methods will take care of the trigonometry for you.
If you still want to know the math behind it, i can make another example.
I have a bit-map image:
( However this should work with any arbitrary image )
I want to take my image and make it a 3D SCNNode. I've accomplished that much with this code. That takes each pixel in the image and creates a SCNNode with a SCNBox geometry.
static inline SCNNode* NodeFromSprite(const UIImage* image) {
SCNNode *node = [SCNNode node];
CFDataRef pixelData = CGDataProviderCopyData(CGImageGetDataProvider(image.CGImage));
const UInt8* data = CFDataGetBytePtr(pixelData);
for (int x = 0; x < image.size.width; x++)
{
for (int y = 0; y < image.size.height; y++)
{
int pixelInfo = ((image.size.width * y) + x) * 4;
UInt8 alpha = data[pixelInfo + 3];
if (alpha > 3)
{
UInt8 red = data[pixelInfo];
UInt8 green = data[pixelInfo + 1];
UInt8 blue = data[pixelInfo + 2];
UIColor *color = [UIColor colorWithRed:red/255.0f green:green/255.0f blue:blue/255.0f alpha:alpha/255.0f];
SCNNode *pixel = [SCNNode node];
pixel.geometry = [SCNBox boxWithWidth:1.001 height:1.001 length:1.001 chamferRadius:0];
pixel.geometry.firstMaterial.diffuse.contents = color;
pixel.position = SCNVector3Make(x - image.size.width / 2.0,
y - image.size.height / 2.0,
0);
[node addChildNode:pixel];
}
}
}
CFRelease(pixelData);
node = [node flattenedClone];
//The image is upside down and I have no idea why.
node.rotation = SCNVector4Make(1, 0, 0, M_PI);
return node;
}
But the problem is that what I'm doing takes up way to much memory!
I'm trying to find a way to do this with less memory.
All Code and resources can be found at:
https://github.com/KonradWright/KNodeFromSprite
Now you drawing each pixel as SCNBox of certain color, that means:
one GL draw per box
drawing of unnecessary two invisible faces between adjancent boxes
drawing N of same 1x1x1 boxes in a row when one box of 1x1xN can be drawn
Seems like common Minecraft-like optimization problem:
Treat your image is 3-dimensional array (where depth is wanted image extrusion depth), each element representing cube voxel of certain color.
Use greedy meshing algorithm (demo) and custom SCNGeometry to create mesh for SceneKit node.
Pseudo-code for meshing algorithm that skips faces of adjancent cubes (simplier, but less effective than greedy meshing):
#define SIZE_X = 16; // image width
#define SIZE_Y = 16; // image height
// pixel data, 0 = transparent pixel
int data[SIZE_X][SIZE_Y];
// check if there is non-transparent neighbour at x, y
BOOL has_neighbour(x, y) {
if (x < 0 || x >= SIZE_X || y < 0 || y >= SIZE_Y || data[x][y] == 0)
return NO; // out of dimensions or transparent
else
return YES;
}
void add_face(x, y orientation, color) {
// add face at (x, y) with specified color and orientation = TOP, BOTTOM, LEFT, RIGHT, FRONT, BACK
// can be (easier and slower) implemented with SCNPlane's: https://developer.apple.com/library/mac/documentation/SceneKit/Reference/SCNPlane_Class/index.html#//apple_ref/doc/uid/TP40012010-CLSCHSCNPlane-SW8
// or (harder and faster) using Custom Geometry: https://github.com/d-ronnqvist/blogpost-codesample-CustomGeometry/blob/master/CustomGeometry/CustomGeometryView.m#L84
}
for (x = 0; x < SIZE_X; x++) {
for (y = 0; y < SIZE_Y; y++) {
int color = data[x][y];
// skip current pixel is transparent
if (color == 0)
continue;
// check neighbour at top
if (! has_neighbour(x, y + 1))
add_face(x,y, TOP, );
// check neighbour at bottom
if (! has_neighbour(x, y - 1))
add_face(x,y, BOTTOM);
// check neighbour at bottom
if (! has_neighbour(x - 1, y))
add_face(x,y, LEFT);
// check neighbour at bottom
if (! has_neighbour(x, y - 1))
add_face(x,y, RIGHT);
// since array is 2D, front and back faces is always visible for non-transparent pixels
add_face(x,y, FRONT);
add_face(x,y, BACK);
}
}
A lot of depends on input image. If it is not big and without wide variety of colors, it I would go with SCNNode adding SCNPlane's for visible faces and then flattenedClone()ing result.
An approach similar to the one proposed by Ef Dot:
To keep the number of draw calls as small as possible you want to keep the number of materials as small as possible. Here you will want one SCNMaterial per color.
To keep the number of draw calls as small as possible make sure that no two geometry elements (SCNGeometryElement) use the same material. In other words, use one geometry element per material (color).
So you will have to build a SCNGeometry that has N geometry elements and N materials where N is the number of distinct colors in your image.
For each color in you image build a polygon (or group of disjoint polygons) from all the pixels of that color
Triangulate each polygon (or group of polygons) and build a geometry element with that triangulation.
Build the geometry from the geometry elements.
If you don't feel comfortable with triangulating the polygons yourself your can leverage SCNShape.
For each polygon (or group of polygons) create a single UIBezierPath and a build a SCNShape with that.
Merge all the geometry sources of your shapes in a single source, and reuse the geometry elements to create a custom SCNGeometry
Note that some vertices will be duplicated if you use a collection of SCNShapes to build the geometry. With little effort you can make sure that no two vertices in your final source have the same position. Update the indexes in the geometry elements accordingly.
I can also direct you to this excellent GitHub repo by Nick Lockwood:
https://github.com/nicklockwood/FPSControls
It will show you how to generate the meshes as planes (instead of cubes) which is a fast way to achieve what you need for simple scenes using a "neighboring" check.
If you need large complex scenes, then I suggest you go for the solution proposed by Ef Dot using a greedy meshing algorithm.
I'm trying to make a copy of the resizing algorithm of OpenCV with bilinear interpolation in C. What I want to achieve is that the resulting image is exactly the same (pixel value) to that produced by OpenCV. I am particularly interested in shrinking and not in the magnification, and I'm interested to use it on single channel Grayscale images. On the net I read that the bilinear interpolation algorithm is different between shrinkings and enlargements, but I did not find formulas for shrinking-implementations, so it is likely that the code I wrote is totally wrong. What I wrote comes from my knowledge of interpolation acquired in a university course in Computer Graphics and OpenGL. The result of the algorithm that I wrote are images visually identical to those produced by OpenCV but whose pixel values are not perfectly identical (in particular near edges). Can you show me the shrinking algorithm with bilinear interpolation and a possible implementation?
Note: The code attached is as a one-dimensional filter which must be applied first horizontally and then vertically (i.e. with transposed matrix).
Mat rescale(Mat src, float ratio){
float width = src.cols * ratio; //resized width
int i_width = cvRound(width);
float step = (float)src.cols / (float)i_width; //size of new pixels mapped over old image
float center = step / 2; //V1 - center position of new pixel
//float center = step / src.cols; //V2 - other possible center position of new pixel
//float center = 0.099f; //V3 - Lena 512x512 lower difference possible to OpenCV
Mat dst(src.rows, i_width, CV_8UC1);
//cycle through all rows
for(int j = 0; j < src.rows; j++){
//in each row compute new pixels
for(int i = 0; i < i_width; i++){
float pos = (i*step) + center; //position of (the center of) new pixel in old map coordinates
int pred = floor(pos); //predecessor pixel in the original image
int succ = ceil(pos); //successor pixel in the original image
float d_pred = pos - pred; //pred and succ distances from the center of new pixel
float d_succ = succ - pos;
int val_pred = src.at<uchar>(j, pred); //pred and succ values
int val_succ = src.at<uchar>(j, succ);
float val = (val_pred * d_succ) + (val_succ * d_pred); //inverting d_succ and d_pred, supposing "d_succ = 1 - d_pred"...
int i_val = cvRound(val);
if(i_val == 0) //if pos is a perfect int "x.0000", pred and succ are the same pixel
i_val = val_pred;
dst.at<uchar>(j, i) = i_val;
}
}
return dst;
}
Bilinear interpolation is not separable in the sense that you can resize vertically and the resize again vertically. See example here.
You can see OpenCV's resize code here.
I have a RotatedRect, I want to do some image processing in the rotated region (say extract the color histogram). How can I get the ROI? I mean get the region(pixels) so that I can do processing.
I find this, but it changes the region by using getRotationMatrix2D and warpAffine, so it doesn't work for my situation (I need to process the original image pixels).
Then I find this suggests using mask, which sounds reasonable, but can anyone teach me how to get the mask as the green RotatedRect below.
Excepts the mask, is there any other solutions ?
Thanks for any hint
Here is my solution, using mask:
The idea is construct a Mat mask by assigning 255 to my RotatedRect ROI.
How to know which point is in ROI (which should be assign to 255)?
I use the following function isInROI to address the problem.
/** decide whether point p is in the ROI.
*** The ROI is a rotated rectange whose 4 corners are stored in roi[]
**/
bool isInROI(Point p, Point2f roi[])
{
double pro[4];
for(int i=0; i<4; ++i)
{
pro[i] = computeProduct(p, roi[i], roi[(i+1)%4]);
}
if(pro[0]*pro[2]<0 && pro[1]*pro[3]<0)
{
return true;
}
return false;
}
/** function pro = kx-y+j, take two points a and b,
*** compute the line argument k and j, then return the pro value
*** so that can be used to determine whether the point p is on the left or right
*** of the line ab
**/
double computeProduct(Point p, Point2f a, Point2f b)
{
double k = (a.y-b.y) / (a.x-b.x);
double j = a.y - k*a.x;
return k*p.x - p.y + j;
}
How to construct the mask?
Using the following code.
Mat mask = Mat(image.size(), CV_8U, Scalar(0));
for(int i=0; i<image.rows; ++i)
{
for(int j=0; j<image.cols; ++j)
{
Point p = Point(j,i); // pay attention to the cordination
if(isInROI(p,vertices))
{
mask.at<uchar>(i,j) = 255;
}
}
}
Done,
vancexu
I found the following post very useful to do the same.
http://answers.opencv.org/question/497/extract-a-rotatedrect-area/
The only caveats are that (a) the "angle" here is assumed to be a rotation about the center of the entire image (not the bounding box) and (b) in the last line below (I think) "rect.center" needs to be transformed to the rotated image (by applying the rotation-matrix).
// rect is the RotatedRect
RotatedRect rect;
// matrices we'll use
Mat M, rotated, cropped;
// get angle and size from the bounding box
float angle = rect.angle;
Size rect_size = rect.size;
// thanks to http://felix.abecassis.me/2011/10/opencv-rotation-deskewing/
if (rect.angle < -45.) {
angle += 90.0;
swap(rect_size.width, rect_size.height);
}
// get the rotation matrix
M = getRotationMatrix2D(rect.center, angle, 1.0);
// perform the affine transformation
warpAffine(src, rotated, M, src.size(), INTER_CUBIC);
// crop the resulting image
getRectSubPix(rotated, rect_size, rect.center, cropped);
If you need a superfast solution, I suggest:
crop a Rect enclosing your RotatedRect rr.
rotate+translate back the cropped image so that the RotatedRect is now equivalent to a Rect. (using warpAffine on the product of the rotation and the translation 3x3 matrices)
Keep that roi of the rotated-back image (roi=Rect(Point(0,0), rr.size())).
It is a bit time-consuming to write though as you need to calculate the combined affine transform.
If you don't care about the speed and want to create a fast prototype for any shape of the region, you can use an openCV function pointPolygonTest() that returns a positive value if the point inside:
double pointPolygonTest(InputArray contour, Point2f pt, bool measureDist)
Simple code:
vector<Point2f> contour(4);
contour[0] = Point2f(-10, -10);
contour[1] = Point2f(-10, 10);
contour[2] = Point2f(10, 10);
contour[3] = Point2f(10, -10);
Point2f pt = Point2f(11, 11);
an double res = pointPolygonTest(contour, pt, false);
if (res>0)
cout<<"inside"<<endl;
else
cout<<"outside"<<endl;