I'm developing an interface in C#. I move the indicators of my vehicle that I will control instantly. Some of these are rotation operations and some are move operations.
These gestures consume a lot of RAM. I'm looking for a way to optimize.
I am open to all your suggestions.
public static Bitmap RotateImage(Image image, float angle)
{
return RotateImage(image, new PointF((float)image.Width / 2, (float)image.Height / 2), angle);
}
public static Bitmap RotateImage(Image image, PointF offset, float angle)
{
if (image == null)
throw new ArgumentNullException("image");
Bitmap rotatedBmp = new Bitmap(image.Width, image.Height);
rotatedBmp.SetResolution(image.HorizontalResolution, image.VerticalResolution);
Graphics g = Graphics.FromImage(rotatedBmp);
g.TranslateTransform(offset.X, offset.Y);
g.RotateTransform(angle);
g.TranslateTransform(-offset.X, -offset.Y);
g.DrawImage(image, new PointF(0, 0));
return rotatedBmp;
}
private void RotateImage(PictureBox pb, Image img, float angle)
{
if (img == null || pb.Image == null)
return;
Image oldImage = pb.Image;
pb.Image = RotateImage(img, angle);
if (oldImage != null)
{
oldImage.Dispose();
}
}
if (foot - first_value_roll == 1)
{
picture.Location = new Point(picture.Location.X, picture.Location.Y + 10);
first_value_roll = foot;
}
else if (foot - first_value_roll == -1)
{
picture.Location = new Point(picture.Location.X, picture.Location.Y - 10);
first_value_roll = foot;
}
C# Image rotation and moving uses a lot of RAM
The code you provided has a memory leak. You did not disposed the graphics object. So in your code:
g.DrawImage(image, new PointF(0, 0));
g.Dispose();
return rotatedBmp;
I think this would resolve the problem.
Related
I am trying to add rotating of image in my WPF application. There is a code:
public static Bitmap RotateImage(Image inputImage, float angleDegrees)
{
if (angleDegrees == 0f || angleDegrees == 360f)
return (Bitmap)inputImage.Clone();
int oldWidth = inputImage.Width;
int oldHeight = inputImage.Height;
int newWidth = inputImage.Width;
int newHeight = inputImage.Height;
Bitmap newBitmap = new Bitmap(newWidth, newHeight, PixelFormat.Format32bppArgb);
newBitmap.SetResolution(inputImage.HorizontalResolution, inputImage.VerticalResolution);
using (Graphics graphicsObject = Graphics.FromImage(newBitmap))
{
graphicsObject.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphicsObject.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphicsObject.SmoothingMode = SmoothingMode.HighQuality;
graphicsObject.TranslateTransform(newWidth / 2f, newHeight / 2f);
graphicsObject.RotateTransform(angleDegrees);
graphicsObject.TranslateTransform(-oldWidth / 2f, -oldHeight / 2f);
graphicsObject.DrawImage(inputImage, 0, 0);
}
return newBitmap;
}
Original code: https://stackoverflow.com/a/16027561/11601385
Well, the image is rotated correctly, but there is a problem with image size.
How can I change size of rotated image to make it correct?
So I'm trying to find a certain pattern in the middle of the screen in a given area. I'm using the AutoItX library and the PixelSearch method.
Rectangle X: 1980
Rectangle Y: 630
Rectangle Size X: 1240
Rectangle Size Y: 180
It's not returning that the pattern has been found, but if I adjust the cords of the rectangle to 0, 0 it shows that the pattern has been found.
The following script used:
public void MonsterScan()
{
if(SixStarMax() == true)
{
Console.WriteLine("Pattern found");
}
}
public bool SixStarMax()
{
Rectangle rect = new Rectangle(1980, 630, 1240, 180);
autoSumPoint = AutoItX.PixelSearch(rect, 0xF8F0E0); // 0xF8F0E0
autoSumPoint2 = AutoItX.PixelSearch(rect, 0xB7AD9F); // 0xB7AD9F
autoSumPoint3 = AutoItX.PixelSearch(rect, 0xCDC6B8); // 0xCDC6B8
autoSumPoint4 = AutoItX.PixelSearch(rect, 0x949084); // 0x949084
if (rect.Contains(autoSumPoint2) == true && rect.Contains(autoSumPoint2) == true && rect.Contains(autoSumPoint3) == true && rect.Contains(autoSumPoint4) == true)
{
AutoItX.MouseMove(autoSumPoint.X, autoSumPoint.Y);
return true;
}
else
{
return false;
}
}
Edit:
Tried to adjust the cordinates to my first screen and I get an error thrown.
System.AccessViolationException: 'An attempt was made to read or write to protected memory. This often indicates that other memory is damaged. '
AutoItX' PixelSearch() has a bug. Possible solutions :
See if it's fixed in latest beta.
Change coordinates (an old version had X/Y switched).
Use PixelGetColor().
Find a 3rd party image search dll.
You can code it without any external library and be very fast by reading the bytes internally. Don't forget to include System.Drawing.Imaging and System.Linq in the using statements and compile it with the 'Unsafe' option in Project options.
public bool SixStarMax()
{
Rectangle rect = new Rectangle(1980, 630, 1240, 180);
Bitmap bitmapToScan = GetScreenPart(rect);
Point?[] autoSumPoints = new Point?[4];
autoSumPoints[0] = SearchForColor(bitmapToScan, 0xF8F0E0);
autoSumPoints[1] = SearchForColor(bitmapToScan, 0xB7AD9F);
autoSumPoints[2] = SearchForColor(bitmapToScan, 0xCDC6B8);
autoSumPoints[3] = SearchForColor(bitmapToScan, 0x949084);
//return true if all points return a value
bool containsAll = autoSumPoints.All(p => p.HasValue);
if (containsAll) Cursor.Position = autoSumPoints[0].Value;
return containsAll;
}
public Bitmap GetScreenPart(Rectangle rect)
{
//initialize bitmap
Bitmap bmp = new Bitmap(rect.Width, rect.Height, PixelFormat.Format32bppArgb);
//fill bitmap
using (Graphics g = Graphics.FromImage(bmp))
g.CopyFromScreen(new Point(rect.Left, rect.Top), new Point(rect.Right, rect.Bottom), rect.Size);
return bmp;
}
public Point? SearchForColor(Bitmap image, uint color)
{
Rectangle rect = new Rectangle(0, 0, image.Width, image.Height);
BitmapData data = image.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format32bppRgb);
//works for 32-bit pixel format only
int ymin = rect.Top, ymax = Math.Min(rect.Bottom, image.Height);
int xmin = rect.Left, xmax = Math.Max(rect.Right, image.Width) - 1;
int strideInPixels = data.Stride / 4; //4 bytes per pixel
unsafe
{
uint* dataPointer = (uint*)data.Scan0;
for (int y = ymin; y < ymax; y++)
for (int x = xmin; x < xmax; x++)
{
//works independently of the data.Stride sign
uint* pixelPointer = dataPointer + y * strideInPixels + x;
uint pixel = *pixelPointer;
bool found = pixel == color;
if (found)
{
image.UnlockBits(data);
return new Point(x, y);
}
}
}
image.UnlockBits(data);
return null;
}
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 two points on my form, and a picturebox, like this:
*
[^]
[ ]
*
I would like to align the picturebox with the points, so that it looks like this:
*
\^\
\ \
*
How would I calculate the angle and how would I rotate the PictureBox?
Currently I'm using this:
double xDifference = Math.Abs(point2.X - point1.X);
double yDifference = Math.Abs(point2.Y - point1.Y);
double angle = Math.Atan(yDifference / xDifference) * 180 / Math.PI;
But that doesn't work since the x and y values are absolute, and thus they can't calculate it if point 2 is left of point 1.
To rotate the image, I found the following function:
public Bitmap rotateImage(Image image, PointF offset, float angle) {
// Create a new empty bitmap to hold rotated image
Bitmap rotatedBmp = new Bitmap(image.Width, image.Height);
rotatedBmp.SetResolution(image.HorizontalResolution, image.VerticalResolution);
// Make a graphics object from the empty bitmap
Graphics g = Graphics.FromImage(rotatedBmp);
// Put the rotation point in the center of the image
g.TranslateTransform(offset.X, offset.Y);
// Rotate the image
g.RotateTransform(angle);
// Move the image back
g.TranslateTransform(-offset.X, -offset.Y);
// Draw passed in image onto graphics object
g.DrawImage(image, new PointF(0, 0));
return rotatedBmp;
}
How would I use that function? I'm not sure what values to insert for offset.
Thanks
I do not like to use angle when it is not necessary.
Here, you just want to change of orthonormal basis.
From {X;Y} you want to move to {U;V} where V (of norm 1) is parallel to AB (or point1 point2).
Because {U;V} is an orthonormal basis, Ux = Vy and Uy = -Vx.
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
namespace CsiChart
{
public partial class CustomControl1 : Control
{
private const float EPSILON = 1e-6f;
private Image _image;
private ImageLayout _imageLayout = ImageLayout.Center;
private PointF _pointA = new PointF(0, 100);
private PointF _pointB = new PointF(100, 0);
public CustomControl1()
{
InitializeComponent();
}
public Image Image
{
get { return _image; }
set
{
if (Equals(_image, value)) return;
_image = value;
Invalidate();
OnImageChanged(EventArgs.Empty);
}
}
public event EventHandler ImageChanged;
public ImageLayout ImageLayout
{
get { return _imageLayout; }
set
{
if (Equals(_imageLayout, value)) return;
_imageLayout = value;
Invalidate();
OnImageLayoutChanged(EventArgs.Empty);
}
}
public event EventHandler ImageLayoutChanged;
public PointF PointA
{
get { return _pointA; }
set
{
if (Equals(_pointA, value)) return;
_pointA = value;
Invalidate();
OnPointAChanged(EventArgs.Empty);
}
}
public event EventHandler PointAChanged;
public PointF PointB
{
get { return _pointB; }
set
{
if (Equals(_pointB, value)) return;
_pointB = value;
Invalidate();
OnPointBChanged(EventArgs.Empty);
}
}
public event EventHandler PointBChanged;
protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint(pe);
if (DesignMode) return;
var g = pe.Graphics;
g.Clear(BackColor);
var image = Image;
if (image == null) return;
var clientRectangle = ClientRectangle;
var centerX = clientRectangle.X + clientRectangle.Width / 2;
var centerY = clientRectangle.Y + clientRectangle.Height / 2;
var srcRect = new Rectangle(new Point(0, 0), image.Size);
var pointA = PointA;
var pointB = PointB;
// Compute U, AB vector normalized.
var vx = pointB.X - pointA.X;
var vy = pointB.Y - pointA.Y;
var vLength = (float) Math.Sqrt(vx*vx + vy*vy);
if (vLength < EPSILON)
{
vx = 0;
vy = 1;
}
else
{
vx /= vLength;
vy /= vLength;
}
var oldTransform = g.Transform;
// Change basis to U,V
// We also take into acount the inverted on screen Y.
g.Transform = new Matrix(-vy, vx, -vx, -vy, centerX, centerY);
var imageWidth = image.Width;
var imageHeight = image.Height;
RectangleF destRect;
switch (ImageLayout)
{
case ImageLayout.None:
destRect = new Rectangle(0, 0, imageWidth, imageHeight);
break;
case ImageLayout.Center:
destRect = new Rectangle(-imageWidth/2, -imageHeight/2, imageWidth, imageHeight);
break;
case ImageLayout.Zoom:
// XY aligned bounds size of the image.
var imageXSize = imageWidth*Math.Abs(vy) + imageHeight*Math.Abs(vx);
var imageYSize = imageWidth*Math.Abs(vx) + imageHeight*Math.Abs(vy);
// Get best scale to fit.
var s = Math.Min(clientRectangle.Width/imageXSize, clientRectangle.Height/imageYSize);
destRect = new RectangleF(-imageWidth*s/2, -imageHeight*s/2, imageWidth*s, imageHeight*s);
break;
default:
throw new InvalidOperationException();
}
g.DrawImage(image, destRect, srcRect, GraphicsUnit.Pixel);
g.Transform = oldTransform;
}
protected virtual void OnImageChanged(EventArgs eventArgs)
{
var handler = ImageChanged;
if (handler == null) return;
handler(this, eventArgs);
}
protected virtual void OnImageLayoutChanged(EventArgs eventArgs)
{
var handler = ImageLayoutChanged;
if (handler == null) return;
handler(this, eventArgs);
}
private void OnPointAChanged(EventArgs eventArgs)
{
var handler = PointAChanged;
if (handler == null) return;
handler(this, eventArgs);
}
private void OnPointBChanged(EventArgs eventArgs)
{
var handler = PointBChanged;
if (handler == null) return;
handler(this, eventArgs);
}
}
}
Let's put all the computations together.
First of all, the direction of the line connecting the two points can be computed by
double xDifference = point2.X - point1.X;
double yDifference = point2.Y - point1.Y;
double angleRadians = Math.Atan2(yDifference, xDifference);
Then, the vertical direction (90 degrees) must be parallel to the direction considered above after the rotation, so the rotation angle is
double rotationAngleRadians = angleDegrees - Math.PI/2;
Having this angle, we can compute the bounding box's size:
double newWidth = image.Width * Math.Abs(Math.Cos(rotationAngleRadians)) +
image.Height * Math.Abs(Math.Sin(rotationAngleRadians));
double newHeight = image.Width * Math.Abs(Math.Sin(rotationAngleRadians)) +
image.Height * Math.Abs(Math.Cos(rotationAngleRadians));
Now, we need first to transform in such a way that the middle of the old image is at position 0. This makes translate transform by (-image.Width/2, -image.Height/2). Then, we apply rotation by the rotationAngleDegrees (which is rotationAngleRadians * 180 / Math.PI), as Graphics' rotation expects angle in degrees. Then, we shift the image to be in the middle of the new image, that is translate transform by (newWidth/2, newHeight/2).
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!