Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to rotate image x degrees in c#?

I have done some searching and i can not find any function thats doing what i whant it todo.

I have a image file of a scanned document with text, but the document is some degrees rotated so i whant to rotate it so the text being inline with each other.

In a perfect world its should be a function doing this automaticly but i can not find anything and what i understand to get it to work automaticly its needed to be some analyze of the image and i think its to big thing todo.

But then i have done a tool to rotate the image on a website manually, but now i need a function to save the rotation to the image file.

This seems to be some differents methods for but no one i tested doing what i whant.

The function i have finded that works almost like i whant is:

public static Bitmap RotateImg(Bitmap bmp, float angle, Color bkColor) {
int w = bmp.Width;
int h = bmp.Height;
PixelFormat pf = default(PixelFormat);
if (bkColor == Color.Transparent)
{
    pf = PixelFormat.Format32bppArgb;
}
else
{
    pf = bmp.PixelFormat;
}

Bitmap tempImg = new Bitmap(w, h, pf);
Graphics g = Graphics.FromImage(tempImg);
g.Clear(bkColor);
g.DrawImageUnscaled(bmp, 1, 1);
g.Dispose();

GraphicsPath path = new GraphicsPath();
path.AddRectangle(new RectangleF(0f, 0f, w, h));
Matrix mtrx = new Matrix();
//Using System.Drawing.Drawing2D.Matrix class 
mtrx.Rotate(angle);
RectangleF rct = path.GetBounds(mtrx);
Bitmap newImg = new Bitmap(Convert.ToInt32(rct.Width), Convert.ToInt32(rct.Height), pf);
g = Graphics.FromImage(newImg);
g.Clear(bkColor);
g.TranslateTransform(-rct.X, -rct.Y);
g.RotateTransform(angle);
g.InterpolationMode = InterpolationMode.HighQualityBilinear;
g.DrawImageUnscaled(tempImg, 0, 0);
g.Dispose();
tempImg.Dispose();
return newImg; }

But this do not change the height and width of the image file so the image file is in the same size but the image "object" has been scaled and rotated.

Any idea how i can do this good?

Answer I find the solution that worked with my image that has a resolution on 300 at a old answer here.

like image 994
RickardP Avatar asked Jan 06 '13 17:01

RickardP


People also ask

How do I rotate a picture with a certain degree?

Manually rotate a picture or shape Select the picture or shape. Manually rotate the text box by selecting the shape or picture rotation handle and dragging in the direction you want. To keep the rotation to 15 degree angles, press and hold Shift while you drag the rotation handle.

How do you rotate a 45 degree line?

If we represent the point (x,y) by the complex number x+iy, then we can rotate it 45 degrees clockwise simply by multiplying by the complex number (1−i)/√2 and then reading off their x and y coordinates.


2 Answers

If I've understood your question correctly, you essentially want to work out the new size of an image once rotated, and how to position the rotated image in it's new bitmap.

enter image description here

The diagram hopefully helps make clear the solution. Here is a bit of pseudo code:

sinVal = abs(sin(angle))
cosVal = abs(cos(angle))
newImgWidth = sinVal * oldImgHeight + cosVal * oldImgWidth
newImgHeight = sinVal * oldImgWidth + cosVal * oldImgHeight
originX = 0
originY = sinVal * oldImgWidth

You want to make the new image from the newImgWidth and newImgHeight, and then perform a rotation around the origin (originX, originY) and then render the image to this point. This will fall over if the angle (in degrees) isn't between -90 and 0 degrees (depicted). If it is between 0 and 90 degrees, then you just change the origin:

originX = sinVal * oldImgHeight
originY = 0

If it is in the range 90 degrees to 270 degrees (-90 degrees) then it is a little tricker (see example code below).

Your code re-written (briefly tested) - it is slightly dodgy but seems to work:

public static Bitmap RotateImg(Bitmap bmp, float angle, Color bkColor)
{
    angle = angle % 360;
    if (angle > 180)
        angle -= 360;

    System.Drawing.Imaging.PixelFormat pf = default(System.Drawing.Imaging.PixelFormat);
    if (bkColor == Color.Transparent)
    {
        pf = System.Drawing.Imaging.PixelFormat.Format32bppArgb;
    }
    else
    {
        pf = bmp.PixelFormat;
    }

    float sin = (float)Math.Abs(Math.Sin(angle * Math.PI / 180.0)); // this function takes radians
    float cos = (float)Math.Abs(Math.Cos(angle * Math.PI / 180.0)); // this one too
    float newImgWidth = sin * bmp.Height + cos * bmp.Width;
    float newImgHeight = sin * bmp.Width + cos * bmp.Height;
    float originX = 0f;
    float originY = 0f;

    if (angle > 0)
    {
        if (angle <= 90)
            originX = sin * bmp.Height;
        else
        {
            originX = newImgWidth;
            originY = newImgHeight - sin * bmp.Width;
        }
    }
    else
    {
        if (angle >= -90)
        originY = sin * bmp.Width;
        else
        {
            originX = newImgWidth - sin * bmp.Height;
            originY = newImgHeight;
        }
    }

    Bitmap newImg = new Bitmap((int)newImgWidth, (int)newImgHeight, pf);
    Graphics g = Graphics.FromImage(newImg);
    g.Clear(bkColor);
    g.TranslateTransform(originX, originY); // offset the origin to our calculated values
    g.RotateTransform(angle); // set up rotate
    g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBilinear;
    g.DrawImageUnscaled(bmp, 0, 0); // draw the image at 0, 0
    g.Dispose();

    return newImg;
}

Note the Degrees to Radians Conversion (180 Degrees == Pi Radians) for the trig functions

Edit: big issue was negative sin values, and me getting width/height confused when calculating origin x/y - this should work fine now (tested)

Edit: modified code to handle any angle

like image 169
VisualMelon Avatar answered Sep 18 '22 12:09

VisualMelon


This is strictly a comment to the nice answer by VisualMelon above, But I'm not allowed to add comments...

There are two tiny bugs in the code

a) The first check after the modulus should either be split into two, or changed to e.g.

if (180<Math.Abs(angle)) angle -= 360*Math.Sign(angle);

Otherwise angles between -360 and -180 will fail, as only +180 to +360 were handled

b) Just after the newImg assignment, a resolution assignment is missing, e.g.

newImg.SetResolution(bmp.HorizontalResolution, bmp.VerticalResolution);

If omitted the image will be scaled if the source is not 96 dpi.

....And splitting sticks, the intermediate calculations of dimensions and offsets ought to be kept in double, and only reduced to float last

like image 45
Eske Rahn Avatar answered Sep 20 '22 12:09

Eske Rahn