I'am using the code below to zoom in/out an image in a picturebox, however after i zoom in and out the x,y data changes. For example, before zooming out, when I draw a rectangle on an object in the image the X,Y,W,H will be 50,50,10,10
But after i zoom out a bit the data changes to 20,20,3,3.
Due to this the roi shifts. I need the zoom option as the image is a bit large.
BTW, im working in EMGUCV and Accord in a single project, so cant use the emgucv imagebox.
My code :
Image zoom1(Image img, Size size)
{
Bitmap bmp = new Bitmap(img, img.Width + (img.Width * size.Width /
100), img.Height + (img.Height * size.Height / 100));
Graphics g = Graphics.FromImage(bmp);
g.InterpolationMode =
System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
return bmp;
}
private void trackBar1_Scroll(object sender, EventArgs e)
{
if (trackBar1.Value > 0 | trackBar1.Value < 0)
{
pictureBox1.Image = zoom1(imgoriginal, new
Size(trackBar1.Value, trackBar1.Value));
}
}
I'm trying to rotate photo with SkiaSharp to 90 degrees with following code:
public SKBitmap Rotate()
{
var bitmap = SKBitmap.Decode("test.jpg");
using (var surface = new SKCanvas(bitmap))
{
surface.RotateDegrees(90, bitmap.Width / 2, bitmap.Height / 2);
surface.DrawBitmap(bitmap.Copy(), 0, 0);
}
return bitmap;
}
But when I save bitmap to JPEG file, it has margins both on top and bottom of image.
Original image: http://imgur.com/pGAuko8.
Rotated image: http://imgur.com/bYxpmI7.
What am I doing wrong?
You may want to do something like this:
public static SKBitmap Rotate()
{
using (var bitmap = SKBitmap.Decode("test.jpg"))
{
var rotated = new SKBitmap(bitmap.Height, bitmap.Width);
using (var surface = new SKCanvas(rotated))
{
surface.Translate(rotated.Width, 0);
surface.RotateDegrees(90);
surface.DrawBitmap(bitmap, 0, 0);
}
return rotated;
}
}
The reason for this (or yours not working as expected) is that you are rotating the bitmap on itself. You have basically taken an image, and then made a copy on draw it onto the first image. Thus, you still have the margins from the image below.
What I did was to create a NEW bitmap and then draw the decoded bitmap onto that.
The second "issue" is that you are rotating the image, but you are not changing the canvas dimensions. If the bitmap is 50x100, and then you rotate 90 degrees, the bitmap is now 100x50. As you can't actually change the dimensions of a bitmap once created, you have to create a new one. You can see this in the output image as it is actually cropped off a bit.
Hope this helps.
Matthew's solution works for me too, but i had an issue when i tried to rotate bitmaps more than 90° or -90° (bitmap was drawed "out of display"). I highly recommend using this solution. Slightly modified result:
public static SKBitmap Rotate(SKBitmap bitmap, double angle)
{
double radians = Math.PI * angle / 180;
float sine = (float)Math.Abs(Math.Sin(radians));
float cosine = (float)Math.Abs(Math.Cos(radians));
int originalWidth = bitmap.Width;
int originalHeight = bitmap.Height;
int rotatedWidth = (int)(cosine * originalWidth + sine * originalHeight);
int rotatedHeight = (int)(cosine * originalHeight + sine * originalWidth);
var rotatedBitmap = new SKBitmap(rotatedWidth, rotatedHeight);
using (var surface = new SKCanvas(rotatedBitmap))
{
surface.Translate(rotatedWidth / 2, rotatedHeight / 2);
surface.RotateDegrees((float)angle);
surface.Translate(-originalWidth / 2, -originalHeight / 2);
surface.DrawBitmap(bitmap, new SKPoint());
}
return rotatedBitmap;
}
In my case I used this when I needed rotate picture on the Xamarin.iOS platform (who ever tried this, knows), works like a charm.
I have the following code which i wrote to try and rotate a bitmap(this is a test) the idea is to take a bitmap and rotate it by some amount of degrees and then draw it on the screen using win forms
protected override void OnDoubleClick(EventArgs e)
{
base.OnDoubleClick(e);
Graphics g = this.CreateGraphics();
Bitmap b = new Bitmap(path);
g.Clear(Color.White);
imagePosition = Cursor.Position;
b = RotateImage(b, 45);
g.DrawImage(b, new Point(100, 100));
}
public Bitmap RotateImage(Bitmap b, float angle)
{
Bitmap returnBitmap = new Bitmap(b.Width, b.Height);
returnBitmap.SetResolution(b.HorizontalResolution, b.VerticalResolution);
Graphics g = Graphics.FromImage(returnBitmap);
g.TranslateTransform((float)b.Width / 2, (float)b.Height / 2);
g.RotateTransform(angle);
g.TranslateTransform(-(float)b.Width / 2, -(float)b.Height / 2);
g.DrawImage(b, new Point(0, 0));
return returnBitmap;
}
this is the image before rotation
and this is the image after rotating 45 degrees like was shown in the code
The reason it's getting clipped is that there isn't enough space to display it rotated. The diagonal is longer than the sides (Pythagoras).
You need to make more space for the image, then it should display OK. How you do that will depend on what the image is contained in.
So I have a picturebox with a background image of size 123x123 pixels.
Everytime the image is clicked I make it rotate by a certain angle using the function below.
//The original image to rotate.
private readonly Bitmap _origPowerKnob = Properties.Resources.PowerKnob;
//Calling the rotation function.
using (Bitmap b = new Bitmap(_origPowerKnob))
{
Bitmap newBmp = RotateImage(b, _powerAngle);
PowerKnob.BackgroundImage = newBmp;
}
//Do the rotation.
private Bitmap RotateImage(Bitmap b, float angle)
{
//Create a new empty bitmap to hold rotated image.
Bitmap returnBitmap = new Bitmap(b.Width, b.Height);
//Make a graphics object from the empty bitmap.
Graphics g = Graphics.FromImage(returnBitmap);
//move rotation point to center of image.
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.TranslateTransform(b.Width / 2F, b.Height / 2F);
//Rotate.
g.RotateTransform(angle);
//Move image back.
g.TranslateTransform(-b.Width / 2F, -b.Height / 2F);
//Draw passed in image onto graphics object.
g.DrawImage(b, new PointF(0,0));
return returnBitmap;
}
The problem is the image in not correctly rotating around the center. It is a little off and I can't seem to understand why. Any suggestions?
I have a small error when trying to rotate an image within a picture box.
It all works. But when rotating, it doesn't rotate perfectly around the center. It's slightly off (not very noticeable) but kinda annoying. Here is my code:
private readonly Bitmap _origPowerKnob = Properties.Resources.PowerKnob;
//CODE WHERE ROTATE METHOD IS CALLED//
using (Bitmap b = new Bitmap(_origPowerKnob))
{
Bitmap newBmp = RotateImage(b, _powerAngle);
PowerKnob.BackgroundImage = newBmp;
}
private Bitmap RotateImage(Bitmap b, float angle)
{
//Create a new empty bitmap to hold rotated image.
Bitmap returnBitmap = new Bitmap(b.Width, b.Height);
//Make a graphics object from the empty bitmap.
Graphics g = Graphics.FromImage(returnBitmap);
//move rotation point to center of image.
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.TranslateTransform((float) b.Width / 2, (float)b.Height / 2);
//Rotate.
g.RotateTransform(angle);
//Move image back.
g.TranslateTransform(-(float)b.Width / 2, -(float)b.Height / 2);
//Draw passed in image onto graphics object.
g.DrawImage(b, new Point(0, 0));
return returnBitmap;
}
Pictures showing what I mean:
It doesn't rotate perfectly. Is there a solution to this? Something I haven't set for my picturebox properties? I've tried alot.
Thanks.
I found a solution to get the rotated image from it's center
[My Testing Conditions]
I'm testing this with the next considerations, if it works for you its ok :3
1.-The source image i used is a perfect square[LxL], taked from a png file, size of the square no matter, just needs to be LxL; //(Width == Height) = true;
2.- The png source image file that i am rotating is transparent and square shaped, i got it from illustrator
3.- I tested files of size 417x417, and 520x520, and 1024x1024
[The code i can share to you]
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace YourNamespace
{
public static class TheClassinWhichYouWantToPlaceTheFunction
{
public static Bitmap RotateImageN(Bitmap b, float angle)
{
//Create a new empty bitmap to hold rotated image.
Bitmap returnBitmap = new Bitmap(b.Width, b.Height);
//Make a graphics object from the empty bitmap.
Graphics g = Graphics.FromImage(returnBitmap);
//move rotation point to center of image.
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
g.TranslateTransform((float)b.Width / 2, (float)b.Height / 2);
//Rotate.
g.RotateTransform(angle);
//Move image back.
g.TranslateTransform(-(float)b.Width / 2, -(float)b.Height / 2);
//Draw passed in image onto graphics object.
//Found ERROR 1: Many people do g.DwarImage(b,0,0); The problem is that you re giving just the position
//Found ERROR 2: Many people do g.DrawImage(b, new Point(0,0)); The size still not present hehe :3
g.DrawImage(b, 0,0,b.Width, b.Height); //My Final Solution :3
return returnBitmap;
}
}
}
I just gived the name "RotateImageN" to the function, because is the 5th solution I have tried :3 i Hope to be helpful
Sorry for my english and/or grammar hehe :3
This is where a simple test form would have helped you a lot.
Take this code and put it in a new WinForms project's Form1.cs file.
using System;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
namespace RotateImage {
public partial class Form1 : Form {
private readonly Graphics gfx;
private readonly Bitmap originalBitmap;
private readonly Bitmap redrawnBitmap;
private readonly Stopwatch sw;
private Timer timer;
public Form1() {
InitializeComponent();
BackColor = Color.White;
timer = new Timer();
timer.Interval = 16;
timer.Enabled = true;
timer.Tick += timer_Tick;
sw = new Stopwatch();
sw.Start();
gfx = CreateGraphics();
originalBitmap = new Bitmap(256, 256);
redrawnBitmap = new Bitmap(256, 256);
using (var bmpGfx = Graphics.FromImage(originalBitmap)) {
DrawCross(bmpGfx, new Point(128, 128), 128D, 0D);
}
}
private void timer_Tick(object sender, EventArgs e) {
// Rotate a full 90 degrees every 4 seconds.
var angle = sw.Elapsed.TotalSeconds * 22.5D;
var newBitmap = RotateImage(originalBitmap, (float)angle);
// Clear the result of the last draw.
gfx.FillRectangle(Brushes.White, new Rectangle(0, 0, 256, 256));
gfx.DrawImageUnscaled(newBitmap, 0, 0);
gfx.DrawEllipse(Pens.Blue, new Rectangle(124, 124, 8, 8));
using (var redrawGfx = Graphics.FromImage(redrawnBitmap)) {
// Clear what we have, we are redrawing on the same surface.
redrawGfx.Clear(Color.White);
DrawCross(redrawGfx, new Point(128, 128), 128D, angle);
}
gfx.DrawImageUnscaled(redrawnBitmap, 256, 0);
gfx.DrawEllipse(Pens.Blue, new Rectangle(256+124, 124, 8, 8));
}
private void DrawCross(Graphics drawGfx, Point center, double radius, double angle) {
// Turn our angle from degrees to radians.
angle *= Math.PI / 180;
// NOTE: Using PointF to lazily "fix" rounding errors and casting (flooring) double to int. When the result of the math below is say 127.9999999...
// then it would get rounded down to 127. There is always Math.Round, which can round to nearest whole (away from .5) integer!
// Draw one line of our cross.
drawGfx.DrawLine(
Pens.Red,
new PointF((float)(Math.Cos(angle) * radius + center.X), (float)(Math.Sin(angle) * radius + center.Y)),
new PointF((float)(Math.Cos(angle - Math.PI) * radius + center.X), (float)(Math.Sin(angle - Math.PI) * radius + center.Y)));
// Rotate our angle 90 degrees.
angle += Math.PI / 2D;
// Draw the other line of our cross.
drawGfx.DrawLine(
Pens.Red,
new PointF((float)(Math.Cos(angle) * radius + center.X), (float)(Math.Sin(angle) * radius + center.Y)),
new PointF((float)(Math.Cos(angle - Math.PI) * radius + center.X), (float)(Math.Sin(angle - Math.PI) * radius + center.Y)));
}
// Your method, not mine.
private Bitmap RotateImage(Bitmap b, float angle)
{
//Create a new empty bitmap to hold rotated image.
Bitmap returnBitmap = new Bitmap(b.Width, b.Height);
//Make a graphics object from the empty bitmap.
Graphics g = Graphics.FromImage(returnBitmap);
//move rotation point to center of image.
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.TranslateTransform((float) b.Width / 2, (float)b.Height / 2);
//Rotate.
g.RotateTransform(angle);
//Move image back.
g.TranslateTransform(-(float)b.Width / 2, -(float)b.Height / 2);
//Draw passed in image onto graphics object.
g.DrawImage(b, new Point(0, 0));
return returnBitmap;
}
}
}
Observe as the two crosses rotate about their center just fine. The left one being a bitmap that is rotated with your method, the right one being redrawn every frame.
That is, there is nothing wrong with your rotation code but it's possible there's something wrong with your source bitmap or your display container.
When you use transparent png with just a portion of the image filled with color for example - the code will rotate transform only the portion of the png that has data in it...
I tried to get a dot in the top center of a png to rotate around the center of the image (500x500 px) and it just recognizes the part that was colored, so it turned into a bounce effect.
I tried with a fully colored 500x500 px image too, and that worked normally..
Hope it helps a little!