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 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.
I have an image, which I want to rotate clockwise by a specified number of degrees. I don't want to cut anything off, so I calculate the width and height of the new image based on the specified rotation (a rotation of 45 degrees for example requires a taller and wider image.
//Calculate required size of new image
GraphicsPath path = new GraphicsPath();
path.AddRectangle(new RectangleF(0f, 0f, bmpSource.Width, bmpSource.Height));
Matrix matrix = new Matrix();
matrix.Rotate(iRotationDegrees);
RectangleF rctNewSize = path.GetBounds(matrix);
//Create new image
Bitmap bmpRotated = new Bitmap(Convert.ToInt32(rctNewSize.Width), Convert.ToInt32(rctNewSize.Height));
using (Graphics g = Graphics.FromImage(bmpRotated))
{
//Set rotation point to center of image
g.TranslateTransform(bmpRotated.Width / 2, 0);
g.RotateTransform(iRotationDegrees);
//Draw the rotated image on the bitmap
g.DrawImageUnscaled(bmpSource, 0,0);
}
With an angle of 45 degrees, when I set the TranslateTransform to bmpRotated.Width / 2, 0 the rotated image is not quite centered horizontally, and the bottom left corner is cut off a bit.
I'm missing some math here that is correctly figuring out the appropriate dx/dy values to pass to TranslateTransform.
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!
So basically what I want to do is overlap two .PNG images with transparent backgrounds. One is with a shotgun which rotates to mouse position, and the other is a cartoon character which I want to put behind the shotgun. Now, the problem is everytime I overlap them, the transparent background of the PNG image gets in the way and I can't see the shooter at all.
I have tried putting the shooter in a panel but placing the shotgun picturebox within it screws up the rotation algorithm (makes it rotate very slowly), I have no idea why.
Any help would be apreciated, thanks.
Coding I used:
Rotation algorithm:
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.TranslateTransform((float)b.Width / 2, (float)b.Height / 2);
//rotate
g.RotateTransform((int)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;
}
private float CalcAngle(Point TargetPos)
{
Point ZeroPoint = new Point(pictureBox1.Location.X + pictureBox1.Width / 2, pictureBox1.Location.Y + pictureBox1.Height / 2);
if (TargetPos == ZeroPoint)
{
return 0;
}
double angle;
double deltaX, deltaY;
deltaY = TargetPos.Y - ZeroPoint.Y;
deltaX = TargetPos.X - ZeroPoint.X;
angle = Math.Atan2(deltaY, deltaX) * 180 / Math.PI;
return (float)angle;
}
private void timer1_Tick(object sender, EventArgs e)
{
pictureBox1.Image = (Bitmap)backup.Clone();
//Load an image in from a file
Image image = new Bitmap(pictureBox1.Image);
//Set our picture box to that image
pictureBox1.Image = (Bitmap)backup.Clone();
//Store our old image so we can delete it
Image oldImage = pictureBox1.Image;
//Set angle
angle = CalcAngle(new Point(Cursor.Position.X, Cursor.Position.Y - 10));
//Pass in our original image and return a new image rotated X degrees right
pictureBox1.Image = rotateImage((Bitmap)image, angle);
if (oldImage != null)
{
oldImage.Dispose();
image.Dispose();
}
}
When you are creating a new Bitmap try to use any of the pixel format for 32 bpp or 64 bpp. See the code below:
Bitmap returnBitmap = new Bitmap(b.Width, b.Height, PixelFormat.Format64bppPArgb);
Here I draw three different png files on top of each other onto a panel:
using (Graphics graphic = panel1.CreateGraphics())
{
using (Image image = Image.FromFile(#"D:\tp3.png")) graphic.DrawImage(image, Point.Empty);
using (Image image = Image.FromFile(#"D:\tp2.png")) graphic.DrawImage(image, Point.Empty);
using (Image image = Image.FromFile(#"D:\tp1.png")) graphic.DrawImage(image, Point.Empty);
}
If you create a new BitMap do as #Palak.Maheria said and use a 32bit format with an alpha channel!