How to rotate Image - c#

I'm trying to rotate an Image but couldn't get expected result.
I want rotate image as 90 degree. After executing my code i got unexpected result.
here is my code:
public async Task Rotate(string path, Rect rect, float degrees)
{
int h = (int)Math.Sqrt(rect.Width * rect.Width + rect.Height * rect.Height);
CanvasDevice device = CanvasDevice.GetSharedDevice();
CanvasRenderTarget webCardImage = null;
CanvasBitmap bitmap = null;
var logicalDpi = DisplayInformation.GetForCurrentView().LogicalDpi;
Vector2 endpoint = new Vector2((float)rect.Width / 2, (float)rect.Height / 2);
try
{
webCardImage = new CanvasRenderTarget(device, h, h, logicalDpi);
using (var ds = webCardImage.CreateDrawingSession())
{
ds.Clear(Colors.Transparent);
using (FileStream imageStream = new FileStream(path, FileMode.Open))
{
IRandomAccessStream fileStream = imageStream.AsRandomAccessStream();
bitmap = await CanvasBitmap.LoadAsync(device, fileStream);
}
ICanvasImage image = new Transform2DEffect
{
Source = bitmap,
TransformMatrix = Matrix3x2.CreateRotation(degrees, endpoint),
};
var sourceRect = image.GetBounds(ds);
ds.DrawImage(image, new Rect(rect.X, rect.Y, rect.Width, rect.Height), sourceRect, 1, CanvasImageInterpolation.HighQualityCubic);
}
}
catch (Exception ex)
{
}
//Convert to Image
}
My Code generated Image:

I'm not sure what your code does, but this code should make the trick
BitmapImage bmpImage = new BitmapImage();
bmpImage.BeginInit();
bmpImage.UriSource = new Uri(#"C:\Images\Dock.jpg", UriKind.RelativeOrAbsolute);
bmpImage.EndInit();
TransformedBitmap transformBmp = new TransformedBitmap();
transformBmp.BeginInit();
transformBmp.Source = bmpImage;
RotateTransform transform = new RotateTransform(90);
transformBmp.Transform = transform;
transformBmp.EndInit();
This is for WPF application, for Windows Forms application you can see here:
https://learn.microsoft.com/it-it/dotnet/api/system.drawing.image.rotateflip?view=netframework-4.5
Hope this helps

Related

Image hashing in UWP/C#, the image repeats horizontally after scaling and graying

I am following this tutorial on image hashing.
So far I have achieved the following:
Code:
private async Task<ImageSource> ProcessImageAsync(StorageFile ImageFile)
{
if (ImageFile == null)
throw new ArgumentNullException("ImageFile cannot be null.");
//The new size of processed image.
const int side = 300; //300 is for clarity. Should be 8 or 16 px.
//Initialize bitmap transformations to be applied to the image.
var transform = new BitmapTransform() { ScaledWidth = side, ScaledHeight = side, InterpolationMode = BitmapInterpolationMode.Cubic };
//Get image pixels.
var stream = await ImageFile.OpenStreamForReadAsync();
var decoder = await BitmapDecoder.CreateAsync(stream.AsRandomAccessStream());
var pixelData = await decoder.GetPixelDataAsync(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied, transform, ExifOrientationMode.RespectExifOrientation, ColorManagementMode.ColorManageToSRgb);
var pixels = pixelData.DetachPixelData();
//Initialize writable bitmap.
var wBitmap = new WriteableBitmap((int)decoder.PixelWidth, (int)decoder.PixelHeight);
await wBitmap.SetSourceAsync(stream.AsRandomAccessStream());
//Make it gray
var grayBitmapBuffer = await ConvertToGrayAsync(wBitmap);
//Create a software bitmap from the writable bitmap's pixel buffer.
var sBitmap = SoftwareBitmap.CreateCopyFromBuffer(wBitmap.PixelBuffer, BitmapPixelFormat.Bgra8, side, side, BitmapAlphaMode.Premultiplied);
//Create software bitmap source.
var sBitmapSource = new SoftwareBitmapSource();
await sBitmapSource.SetBitmapAsync(sBitmap);
return sBitmapSource;
}
private async Task<IBuffer> ConvertToGrayAsync(WriteableBitmap srcBitmap)
{
// Get the source bitmap pixels
byte[] srcPixels = new byte[4 * srcBitmap.PixelWidth * srcBitmap.PixelHeight];
using (Stream pixelStream = srcBitmap.PixelBuffer.AsStream())
{
await pixelStream.ReadAsync(srcPixels, 0, srcPixels.Length);
}
// Create a destination bitmap and pixels array
WriteableBitmap dstBitmap = new WriteableBitmap(srcBitmap.PixelWidth, srcBitmap.PixelHeight);
byte[] dstPixels = new byte[4 * dstBitmap.PixelWidth * dstBitmap.PixelHeight];
for (int i = 0; i < srcPixels.Length; i += 4)
{
double b = (double)srcPixels[i] / 255.0;
double g = (double)srcPixels[i + 1] / 255.0;
double r = (double)srcPixels[i + 2] / 255.0;
byte a = srcPixels[i + 3];
double e = (0.21 * r + 0.71 * g + 0.07 * b) * 255;
byte f = Convert.ToByte(e);
dstPixels[i] = f;
dstPixels[i + 1] = f;
dstPixels[i + 2] = f;
dstPixels[i + 3] = a;
}
// Move the pixels into the destination bitmap
using (Stream pixelStream = dstBitmap.PixelBuffer.AsStream())
{
await pixelStream.WriteAsync(dstPixels, 0, dstPixels.Length);
}
dstBitmap.Invalidate();
// Display the new bitmap
return dstBitmap.PixelBuffer;
}
This is the original image which user selects:
And this is the output produced by my code:
It is getting scaled and grayed all-right. But I don't understand why is the image repeating horizontally??
Here is the XAML code that renders the image:
<Image x:Name="myImage" Stretch="None" />
And the C# code that sets the image source:
StorageFile userPickedFile = ...; //code ignored.
myImage.Source = await ProcessImageAsync(userPickedFile);
What am I doing wrong? Something looks fishy about the processed image.. I am lost from here... How should I move ahead with Hashing? Any help?
Your ConvertToGrayAsync method should simply return the converted WriteableBitmap. The pixel conversion code inside the loop could also be simplified, and the method does not need to operate on a source and destination buffer. It could as well manipulate the pixel values in place.
private async Task<WriteableBitmap> ConvertToGrayAsync(WriteableBitmap srcBitmap)
{
var pixels = srcBitmap.PixelBuffer.ToArray();
for (int i = 0; i < pixels.Length; i += 4)
{
var b = pixels[i];
var g = pixels[i + 1];
var r = pixels[i + 2];
var f = (byte)(0.21 * r + 0.71 * g + 0.07 * b);
pixels[i] = f;
pixels[i + 1] = f;
pixels[i + 2] = f;
}
var dstBitmap = new WriteableBitmap(srcBitmap.PixelWidth, srcBitmap.PixelHeight);
using (var pixelStream = dstBitmap.PixelBuffer.AsStream())
{
await pixelStream.WriteAsync(pixels, 0, pixels.Length);
}
return dstBitmap;
}
The following method loads a WriteableBitmap with a predefined size:
private async Task<WriteableBitmap> LoadWriteableBitmapAsync(
StorageFile file, int width, int height)
{
using (var fileStream = await file.OpenReadAsync())
using (var memoryStream = new InMemoryRandomAccessStream())
{
var decoder = await BitmapDecoder.CreateAsync(fileStream);
var transform = new BitmapTransform
{
ScaledWidth = (uint)width,
ScaledHeight = (uint)height,
InterpolationMode = BitmapInterpolationMode.Cubic
};
var pixelData = await decoder.GetPixelDataAsync(
BitmapPixelFormat.Bgra8, BitmapAlphaMode.Straight, transform,
ExifOrientationMode.RespectExifOrientation,
ColorManagementMode.ColorManageToSRgb);
var pixels = pixelData.DetachPixelData();
var encoder = await BitmapEncoder.CreateAsync(
BitmapEncoder.PngEncoderId, memoryStream);
encoder.SetPixelData(
BitmapPixelFormat.Bgra8, BitmapAlphaMode.Straight,
(uint)width, (uint)height, 96, 96, pixels);
await encoder.FlushAsync();
memoryStream.Seek(0);
var bitmap = new WriteableBitmap(width, height);
await bitmap.SetSourceAsync(memoryStream);
return bitmap;
}
}
Your ProcessImageAsync method would now look like this:
private async Task<WriteableBitmap> ProcessImageAsync(
StorageFile file, int width, int height)
{
return await ConvertToGrayAsync(
await LoadWriteableBitmapAsync(file, width, height));
}

How to show dropshadow on drawingcontext tools

Actually i am using drawingContext.DrawRectangle method for drawing rectangle on canvas..
i want to add shadow effect on rectangle..
drawingContext.DrawRectangle(new SolidColorBrush(graphicsObjectFillColor),
new Pen(new SolidColorBrush(ObjectColor), ActualLineWidth),
Rectangle);
or i am using this to add drop shadow..
DropShadowEffect effect = new DropShadowEffect();
effect = new DropShadowEffect { Color = Colors.Black, Direction = -45, Opacity = 0.5, ShadowDepth = 4};
this.Effect = effect;
the shadow is showing but add the time of draw all tool on image tha shodow is not shawing
i am using
DrawingVisual vs = new DrawingVisual();
DrawingContext dc = vs.RenderOpen();
// Draw image
dc.DrawImage(image.Source, rect);
double scale = width / image.Source.Width;
// Keep old existing actual scale and set new actual scale.
double oldActualScale = drawingCanvas.ActualScale;
drawingCanvas.ActualScale = oldActualScale;
// Remove clip in the canvas - we set our own clip.
drawingCanvas.RemoveClip();
// Prepare drawing context to draw graphics
rect = new Rect(left, top, width, height);
dc.PushClip(new RectangleGeometry(rect));
double horizontalScale = Math.Abs((positionDrawingCanvas.X) - (positionImage.X));
double verticalScale = Math.Abs((positionDrawingCanvas.Y) - (positionImage.Y));
double difX = 0.0;
double difY = 0.0;
//if (horizontalScale != 0 && verticalScale != 0)
//{
// //horizontalScale = Math.Abs((positionDrawingCanvas.X + Math.Abs((positionImage.X / sliderScale.Value - positionImage.X))) - (positionImage.X));
// //verticalScale = Math.Abs((positionDrawingCanvas.Y + Math.Abs((positionImage.Y / sliderScale.Value - positionImage.Y))) - (positionImage.Y));
// difX = (positionImage.X - positionImage.X / sliderScale.Value);
// difY = (positionImage.Y - positionImage.Y / sliderScale.Value);
//}
dc.PushTransform(new TranslateTransform(difX + left - horizontalScale, difY+top - verticalScale));
dc.PushTransform(new ScaleTransform(1, 1));
// Ask canvas to draw overlays
drawingCanvas.Draw(dc);
// Restore old actual scale.
drawingCanvas.ActualScale = oldActualScale;
// Restore clip
drawingCanvas.RefreshClip();
dc.Pop();
dc.Pop();
dc.Pop();
dc.Close();
width = (Utilityhelper.GetDIPIndependentHorizontal(rect.Width));
height = (Utilityhelper.GetDIPIndependentVertical(rect.Height));
bmp = new RenderTargetBitmap((int)width, (int)(height), Utilityhelper.graphics.DpiX, Utilityhelper.graphics.DpiY, PixelFormats.Default);
//bmp = new RenderTargetBitmap((int)(scale * (rect.Width)), (int)(scale * (rect.Height)), scale * 96, scale * 96, PixelFormats.Default);
bmp.Render(vs);
sliderScale.Value = oldScale;
//imageBackground.Stretch = Stretch.Uniform;
//drawingCanvas.Width = (Utilityhelper.GetDIPDependentHorizontal(drawingCanvas.Width));
//drawingCanvas.Height = (Utilityhelper.GetDIPDependentVertical(drawingCanvas.Height));
return bmp;
You have to just change your render method
RenderTargetBitmap bmp = new RenderTargetBitmap((int)width, (int)(height), DpiX, DpiY, PixelFormats.Default);
BitmapSource source = null;
if (bmp != null)
{
bmp.Render(image);
bmp.Render(drawingCanvas);
source = bmp;
}
Here is how you get image (bitmap) from visual in wpf:
// render content into image
var render = new RenderTargetBitmap((int)ContentPresenter.RenderSize.Width, (int)ContentPresenter.RenderSize.Height, 96, 96, PixelFormats.Default);
render.Render(ContentPresenter);
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(render));
using (var stream = new MemoryStream())
{
// create bitmap image
encoder.Save(stream);
var bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.StreamSource = stream;
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.EndInit();
bitmap.Freeze();
// use BitmapImage
...
}
ContentPresenter is some visual.

AForge GetObjectsRectangles() gives false output

For me the Aforge rectangle detection gives completely false coordinates.
Here's my code:
public List<System.Drawing.Rectangle> Detect(string path)
{
var image = GetImage(path);
var blobCounter = new BlobCounter();
blobCounter.FilterBlobs = true;
blobCounter.MinWidth = 50;
blobCounter.MinHeight = 50;
blobCounter.ProcessImage(image);
var rects = blobCounter.GetObjectsRectangles();
return rects.ToList();
}
public System.Drawing.Bitmap GetImage(string path)
{
BitmapSource bSource = new BitmapImage(new Uri(path));
var image = Helpers.BitmapConverter.GetBitmap(bSource);
return image;
}
and my test image is this:
I've also tried reverting the colors, but nothing seems to help.
I always get just: {X = 16 Y = 42 Width = 51 Height = 141} That is obviously wrong for that rectangle in the image. How do I use Aforge to detect rectangles?
By using the AForge BlobCounter class GetObjectsInformation method.
Using the image provided, a snippet of the code provided in a simple Windows Forms Application with a picture box and a button I was able to detect the rectangle by drawing a lime green line over the original image using the following code.
//after pictureBox1's image has been set to the provided image.
Bitmap image = new Bitmap(pictureBox1.Image);
BlobCounter blobCounter = new BlobCounter();
blobCounter.FilterBlobs = true;
blobCounter.MinWidth = 50;
blobCounter.MinHeight = 50;
blobCounter.ProcessImage(image);
Blob[] blobs = blobCounter.GetObjectsInformation();
Blob blob = blobs[0];
SimpleShapeChecker shapeChecker = new SimpleShapeChecker();
Graphics g = Graphics.FromImage(image);
Pen redPen = new Pen(Color.Red, 2);
Pen greenPen = new Pen(Color.Lime, 2);
List<IntPoint> edgePoints = blobCounter.GetBlobsEdgePoints(blob);
List<IntPoint> corners;
if (shapeChecker.IsConvexPolygon(edgePoints, out corners))
{
PolygonSubType subType = shapeChecker.CheckPolygonSubType(corners);
Pen pen;
if (subType == PolygonSubType.Unknown)
{
pen = (corners.Count == 4) ? redPen : redPen;
}
else
{
pen = greenPen;
}
System.Drawing.Point[] array = new System.Drawing.Point[corners.Count];
for (int i = 0, n = corners.Count; i < n; i++)
{
array[i] = new System.Drawing.Point(corners[i].X + 1, corners[i].Y + 1);
}
g.DrawPolygon(pen, array);
}
redPen.Dispose();
greenPen.Dispose();
g.Dispose();
pictureBox1.Image = image;
pictureBox1.Width = image.Width;
pictureBox1.Height = image.Height;
Provided Image
Processed Image

Image resize when rotate

I'm trying to rotate the image.. I have a pictureBox 369x276. But when I rotate, this size decrease.
The pictureBox sizeMode is PictureBoxSizeMode.StretchImage
here is my code:
Bitmap oldBitmap = (Bitmap)pictureBox1.Image;
float angle = 90;
var newBitmap = new Bitmap(oldBitmap.Width, oldBitmap.Height);
var graphics = Graphics.FromImage(newBitmap);
graphics.TranslateTransform((float)oldBitmap.Width / 2, (float)oldBitmap.Height / 2);
graphics.RotateTransform(angle);
graphics.TranslateTransform(-(float)oldBitmap.Width / 2, -(float)oldBitmap.Height / 2);
graphics.DrawImage(oldBitmap, new Point(0, 0));
pictureBox1.Image = newBitmap;
Just use RotateFlip:
Bitmap oldBitmap = (Bitmap)pictureBox1.Image;
oldBitmap.RotateFlip(RotateFlipType.Rotate90FlipNone);
pictureBox1.Image = oldBitmap;
As #Dan-o has pointed out, this allows a rotation of any of the degree's in the System.Drawing.RotateFlipType enum.
To rotate a Bitmap any angle without losing the size, you could do the following, but it's a bit convoluted!
One - Add the WriteableBitmapEx library to your project
Two - Add the XAML, WindowsBase and PresentationCore libraries to your project
Three - Use the following to rotate your Bitmap any amount of degrees:
class Program
{
static void Main(string[] args)
{
Bitmap oldBitmap = (Bitmap)pictureBox1.Image;;
var bitmapAsWriteableBitmap = new WriteableBitmap(BitmapToBitmapImage(oldBitmap));
bitmapAsWriteableBitmap.RotateFree(23);
var rotatedImageAsMemoryStream = WriteableBitmapToMemoryStream(bitmapAsWriteableBitmap);
oldBitmap = new Bitmap(rotatedImageAsMemoryStream);
}
public static BitmapImage BitmapToBitmapImage(Bitmap bitmap)
{
var memStream = BitmapToMemoryStream(bitmap);
return MemoryStreamToBitmapImage(memStream);
}
public static MemoryStream BitmapToMemoryStream(Bitmap image)
{
var memoryStream = new MemoryStream();
image.Save(memoryStream, ImageFormat.Bmp);
return memoryStream;
}
public static BitmapImage MemoryStreamToBitmapImage(MemoryStream ms)
{
ms.Position = 0;
var bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.StreamSource = ms;
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.EndInit();
bitmap.Freeze();
return bitmap;
}
private static MemoryStream WriteableBitmapToMemoryStream(WriteableBitmap writeableBitmap)
{
var ms = new MemoryStream();
var encoder = new JpegBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(writeableBitmap));
encoder.Save(ms);
return ms;
}
}
Pain in the ass, but works!
The smaller image size is to be expected. I've never figured out why, but the Graphics.DrawImage really only works if you provide it not only a start location, but also a size. One of the overloads allows you to include size.

WPF Leak when adding multiple items to a canvas and rendering using RenderBitmapTarget

I've been having a whale of a time diagnosing a leak in a service which is rendering multiple images to a canvas and then pushing out a bitmap or PNG at the end (and doing this multiple times). On start up the service will rocket up to 600MB+ and then keep on increasing until it's taken almost all that it can get hold of. It will never decrease once started.
I've instrumented and run the service using the VS2012 perf mon and seen that there are large amounts of Byte arrays laying about after processing has been completed. I've tried a GC clear to see if it would wipe them out to no avail. Looking into it using WinDbg I can see that the byte array is being held onto by a long chain of items (mostly WPF objects) which is keeping the image in ram.
I've had a look over MSDN for all the objects that are being used and can't find anything that points out a problem with the way the code is running.
I've put together some sample code that closely follows the service (some bits reduced for bevity). This is called off a thread (set to STA). The only other differece is the service code uses MemoryStreams to load images sourced from a DB rather than the URIs I am using. The streams are disposed of:
public static void TestThread()
{
const int rounds = 50;
const int innerRounds = 50;
var randomiser = new Random(DateTime.Now.Millisecond);
// Simulating some live values
const float scaling = 2.67F;
const int pageWidth = 363;
const int pageHeight = 516;
const int dpi = 96;
// To simulate the live system using multiple images
// This is an list of images of different sizes etc
var imageList = new List<ImageData>
{
new ImageData{Uri = new Uri(#"..."), Height = 2592},
new ImageData{Uri = new Uri(#"..."), Height = 1339},
new ImageData{Uri = new Uri(#"..."), Height = 386},
new ImageData{Uri = new Uri(#"..."), Height = 968},
new ImageData{Uri = new Uri(#"..."), Height = 1952},
new ImageData{Uri = new Uri(#"..."), Height = 1024},
};
var proc = Process.GetCurrentProcess();
for (var i = 0; i < rounds; ++i)
{
var canvas = new Canvas();
canvas.BeginInit();
canvas.SnapsToDevicePixels = false;
canvas.UseLayoutRounding = false;
canvas.Width = pageWidth;
canvas.Height = pageHeight;
canvas.Background = Brushes.White;
for (var j = 0; j < innerRounds; ++j)
{
var img = new Image {Stretch = Stretch.Fill};
var bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
var imageNo = randomiser.Next(0, imageList.Count - 1);
bitmapImage.UriSource = imageList[imageNo].Uri;
int imageHeight = imageList[imageNo].Height;
bitmapImage.DecodePixelHeight = (int) (imageHeight * scaling * 1.8);
bitmapImage.EndInit();
if (bitmapImage.CanFreeze)
{
bitmapImage.Freeze();
}
var opacityMask = new ImageBrush();
var opactityBitmap = new BitmapImage();
opactityBitmap.BeginInit();
opactityBitmap.CacheOption = BitmapCacheOption.OnLoad;
imageNo = randomiser.Next(0, imageList.Count - 1);
opactityBitmap.UriSource = imageList[imageNo].Uri;
int opacityImageHeight = imageList[imageNo].Height; ;
opactityBitmap.DecodePixelHeight = (int)(opacityImageHeight * scaling * 1.8);
opactityBitmap.EndInit();
if (opactityBitmap.CanFreeze)
{
opactityBitmap.Freeze();
}
opacityMask.ImageSource = opactityBitmap;
img.OpacityMask = opacityMask;
img.Source = bitmapImage;
img.Width = pageWidth * scaling;
img.Height = pageHeight * scaling;
Canvas.SetLeft(img, 0);
Canvas.SetTop(img, 0);
canvas.Children.Add(img);
img.Opacity = 50F;
}
canvas.LayoutTransform = null;
var size = new Size(Math.Max(canvas.Width, 5), Math.Max(canvas.Height, 5));
canvas.Measure(size);
canvas.Arrange(new Rect(size));
canvas.EndInit();
var renderTargetBitmap = new RenderTargetBitmap(pageWidth, pageHeight, dpi, dpi, PixelFormats.Default); //xxx
renderTargetBitmap.Render(canvas);
if (renderTargetBitmap.CanFreeze)
{
renderTargetBitmap.Freeze();
}
System.Drawing.Image imageData;
using (var ms = new MemoryStream())
{
var image = new PngBitmapEncoder();
image.Frames.Add(BitmapFrame.Create(renderTargetBitmap));
image.Save(ms);
imageData = System.Drawing.Image.FromStream(ms);
}
var encoder = Encoder.Quality;
var encoderParams = new EncoderParameters(1);
encoderParams.Param[0] = new EncoderParameter(encoder, 100L);
ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders();
var codecInfo = codecs.FirstOrDefault(x => x.FormatID == ImageFormat.Png.Guid);
Byte[] bitmapArray;
using (var ms = new MemoryStream())
{
imageData.Save(ms, codecInfo, encoderParams);
bitmapArray = ms.GetBuffer();
}
var filepath = string.Format(#"C:\temp\{0}.png", i);
imageData.Save(filepath, ImageFormat.Png);
var gcMemory = GC.GetTotalMemory(false) / 1024;
Console.WriteLine("Proc mem = {0}KB, GC = {1}KB", (proc.PrivateMemorySize64 / 1024), gcMemory);
}
Console.WriteLine("Exiting");
}
I'm hoping it's something obvious I'm missing here. Thanks for all your help!

Categories

Resources