ImageResizer/System.Drawing and CMYK JPEGs - c#

I'm doing some simple image resizing and rotation in an ASP.NET MVC 4 project, using the ImageResizer library. The problem is that when I use it to process 32-bpp CMYK JPEG files, it fails with an ArgumentException, but only on my (Windows Server 2008 R2) server - it works fine on my (Windows Vista) laptop.
The JPEG files in question aren't too large (700x500) or otherwise non-standard - all web browsers and Paint can open them just fine, even on the server in question itself. The JPEGs have been generated using ImageMagick, and everything works fine if I ask ImageMagick to use an RGB colorspace (-colorspace sRGB.)
The weird thing is, just resizing the image works fine in all cases, but it fails if I try to resize and rotate the image on the server.
It looks like some lower-level Win32 or GDI+ function call is what's failing here - here's the relevant part of the stack trace:
[ArgumentException: Parameter is not valid.]
System.Drawing.Graphics.CheckErrorStatus(Int32 status) +1621285
System.Drawing.Graphics.DrawImage(Image image, PointF[] destPoints, RectangleF srcRect, GraphicsUnit srcUnit, ImageAttributes imageAttr, DrawImageAbort callback, Int32 callbackData) +727
System.Drawing.Graphics.DrawImage(Image image, PointF[] destPoints, RectangleF srcRect, GraphicsUnit srcUnit, ImageAttributes imageAttr) +73
ImageResizer.ImageBuilder.RenderImage(ImageState s) +763
ImageResizer.ImageBuilder.Render(ImageState s) +174
ImageResizer.ImageBuilder.Process(ImageState s) +105
ImageResizer.ImageBuilder.buildToBitmap(Bitmap source, ResizeSettings settings, Boolean transparencySupported) +276
ImageResizer.ImageBuilder.buildToStream(Bitmap source, Stream dest, ResizeSettings settings) +149
ImageResizer.ImageBuilder.BuildJob(ImageJob job) +940
ImageResizer.ImageBuilder.Build(ImageJob job) +223
Any ideas?

After some more research and testing, I've found that the problem occurs only if the Bitmap.RotateFlip method has been called on the source bitmap, before it is drawn onto a target bitmap for cropping and resizing.
I ended up having to re-write the code using plain old System.Drawing myself, because there is no way to change the behavior of the ImageResizer library. Specifically, I wrote my own rotation function, which uses a transformation matrix to do the same thing.
private Bitmap RotateImage(Bitmap source, float angle)
{
if (angle == 0) return (Bitmap)source.Clone();
Bitmap target;
using (GraphicsPath path = new GraphicsPath())
using (Matrix matrix = new Matrix())
{
path.AddRectangle(new RectangleF(0.0f, 0.0f, source.Width, source.Height));
matrix.Rotate(angle);
RectangleF rect = path.GetBounds(matrix);
target = new Bitmap((int)Math.Round(rect.Width), (int)Math.Round(rect.Height));
target.SetResolution(source.HorizontalResolution, source.VerticalResolution);
using (Graphics g = Graphics.FromImage(target))
{
g.TranslateTransform(-rect.X, -rect.Y);
g.RotateTransform(angle);
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.DrawImageUnscaled(source, 0, 0);
}
}
return target;
}
I would have wanted to set the PixelFormat of the new Bitmap to the one from the source - but doing so throws weird GDI+ "Out of memory" errors on encountering the CMYK JPEG files in question. This makes me wonder if this odd behavior is linked to this GDI+ bug/quirk that returns different ImageFlags and PixelFormat values on Windows 7/Server 2008 R2 and Windows Vista/Server 2008.

Related

How to render video from ARGB Frames

I'm using the Microsoft.MixedReality.WebRTC library and I am planing on using it for my next project - a Real-Time video chatting app.
I have been able to establish a connection and pass video frames around.
How would I properly render those Frames and display them as Video?
Using WPF's MediaElement seems pretty easy, but I can only input an Uri object as source, I cannot feed it single frames, AFAIK.
I have read that drawing Bitmaps is a possible solution, but I am sure this would mean many hours reinventing the wheel and testing, which I am not a fan of doing, unless there is no other way.
The library works as follows:
Each time a new frame is received by the client the Argb32VideoFrameReady event is raised. A Argb32VideoFrame struct object is then passed to the callback, which contains an IntPtr to the raw data. Height, Width and Stride are also provided.
More Information on the specific struct here
What would be some ways I could achieve this?
I am planning on using WPF.
The solution should target Windows 7+ and .Net Framework 4.6.2.
Thanks in advance.
With an Image element in XAML
<Image x:Name="image"/>
the simple method below would directly copy the frame into a WriteableBitmap that is assigned to the Image's Source property.
private void UpdateImage(Argb32VideoFrame frame)
{
var bitmap = image.Source as WriteableBitmap;
var width = (int)frame.width;
var height = (int)frame.height;
if (bitmap == null ||
bitmap.PixelWidth != width ||
bitmap.PixelHeight != height)
{
bitmap = new WriteableBitmap(
width, height, 96, 96, PixelFormats.Bgra32, null);
image.Source = bitmap;
}
bitmap.WritePixels(
new Int32Rect(0, 0, width, height),
frame.data, height * frame.stride, frame.stride);
}
ARGBVideoFrame from here: https://github.com/microsoft/MixedReality-WebRTC/blob/master/libs/Microsoft.MixedReality.WebRTC/VideoFrame.cs
PixelFormats.Bgra32 seems to be the proper format, due to this comment on the struct:
The ARGB components are in the order of a little endian 32-bit integer, so 0xAARRGGBB, or (B, G, R, A) as a sequence of bytes in memory with B first and A last.

Saving large image to filesystem with emgu CV

I am trying to save a large image (2048 x 2048 pixels) from a C# software to the filesystem using the emgu cv library:
var mat = new Mat( pathToFile, ImreadModes.GrayScale );
mat.Save( "imagename.png" );
The second line of the code snippet throws an exception with the message
System.ArgumentException: Parameter is not valid. at
System.Drawing.Bitmap..ctor(Int32 width, Int32 height, Int32 stride,
PixelFormat format, IntPtr scan0)
I found out, that the size of the image can be a problem, when the save() function creates a bitmap.
What other possibilites do you suggest, to save the Mat-image onto the filesystem?
Note that I the Image is not already stored in the filesystem as my code snippet implies. I am receiving it from a camera.
In fact the problem was not the size of the image. Before saving the image, some strange settings were made to the camera what made all taken pictures unreadable.
Now it works.

Opening an image file to a Bitmap class as 32bppPArgb

Does .NET support PixelFormat conversions? I can't find any methods or constructors to do what I want.
I am allowing the user to choose any image file and I am creating a new Bitmap class from the file.
Then I am drawing the image using custom painting. It's very slow and I am told that the GDI is much faster dealing with images in the 32bppPArgb pixel format.
How do I perform the conversion? Assuming I have a string for the filename which could be a JPEG, TIF, PNG, etc., and I want to load it to a Bitmap class.
Edit:
Here is what I am trying to do, but it's not loading the image properly. The image is not filling the entire bitmap when I draw it.
using (Bitmap tempImage = new Bitmap(filename))
{
Image = new Bitmap(tempImage.Width, tempImage.Height, PixelFormat.Format32bppPArgb);
using (Graphics g = Graphics.FromImage(Image))
{
g.DrawImageUnscaled(tempImage, 0, 0);
}
}
If you have a scaling issue it is not caused by the pixel format. Instead, it is likely caused by DPI settings in the source image file. Try this instead of .DrawImageUnscaled:
g.DrawImage(
tempImage,
new Reactangle( Point.Empty, Image.Size ),
new Reactangle( Point.Empty, Image.Size ),
GraphicsUnit.Pixels );
That's what I always used instead of .DrawImageUnscaled - it has failed me too many times!

Image manipulation

I need the easy to learn & fast method for generating image from background image, texts and after that saving as JPEG format.
What you can suggest? Any library or tutorial on this? Important criteria is simpleness.
in .Net 3.5/4 you can also use WPF/Media.Imaging as an alternative to GDI+
First create a DrawingVisual and a DrawingContext:
DrawingVisual visual = new DrawingVisual();
DrawingContext dc = visual.RenderOpen();
Then draw stuff on it:
dc.DrawRectangle(...);
dc.DrawText(...);
etc...
Make sure you close it:
dc.Close();
The great thing about WPF is everything in the GUI is actually a visual too, so if you prefer you don't have to use the code above to draw programatically, you can actually build up your visual in xaml on a window and then just render that straight to the RenderTargetBitmap.
Once you have built your visual you can render it to a file using an encoder (.Net has encoders for Jpeg, Png, Bmp, Gif, Tiff and Wmp).
// Create a render target to render your visual onto. The '96' values are the dpi's, you can set this as required.
RenderTargetBitmap frame = new RenderTargetBitmap((int)visual.ContentBounds.Width, (int)visual.ContentBounds.Height, 96, 96, PixelFormats.Pbgra32);
frame.Render(visual);
// Now encode the rendered target into Jpeg and output to a file.
JpegBitmapEncoder jpeg = new JpegBitmapEncoder();
jpeg.Frames.Add(BitmapFrame.Create(frame));
using (Stream fs = File.Create(#"c:\filename.jpg"))
{
jpeg.Save(fs);
}
There are some good MS Tutorials on Drawing Objects and WPF Graphics Rendering.
I usually do this using GDI+. There are lots of tutorials on this on the net, but basically what you need to do is something like this:
using(Image image = new Bitmap(Width, Height))
using (Graphics g = Graphics.FromImage(image)) {
g.Draw....
g.Draw....
image.Save(filename, ImageFormat.Jpeg);
}
The calls to Draw.... you can draw primitives, images, text and so forth.
Also remember that is text looks jagged, you have methods on the Graphics object to smooth this out. In this case g.TextRenderingHint = TextRenderingHint.AntiAlias;
There are also other options to make it look better, if you feel it is jagged. The default settings is geared more towards performance than quality, so if you want high quality you need to set this yourself. g.SmoothingMode set to for example HighQuality will make your round primitives look much smoother than the default configuration.
It's really easy to use, and to make the final image look like you want it to, so give it a try!
Instead of good old GDI+ you can use the more modern (and often faster) System.Windows.Media.Imaging APIs.
GDI+ and the System.Drawing namespace are what is required to do what you want. A basic example is below but there are many resources on the net detailing more advanced features:
using(Bitmap myBitmap = new Bitmap("C:\\backgroundImage.jpg"))
using(Graphics g = Graphics.FromImage(myBitmap))
{
g.DrawString("Text", new Font("Arial", 10), Brushes.White, new PointF(0, 0));
myBitmap.Save("C:\\newImage.jpg");
}

Mono Ignores Graphics.InterpolationMode?

I have a program that draws some vector graphics using System.Drawing and the Graphics class. The anti-aliasing works, kindof okay, but for my need I neede oversampling, so I create the starting image to be n times larger and then scale back the final image by n. On Window and .NET the resulting image looks great! However, on Mono 2.4.2.3 (Ubuntu 9.10 stock install), the intropolation is horrible. Here's how I'm scaling my images:
Bitmap bmp = new Bitmap(Bmp.Width / OverSampling, Bmp.Height / OverSampling);
Graphics g = Graphics.FromImage(bmp);
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.DrawImage(Bmp, 0, 0, bmp.Width, bmp.Height);
g.Dispose();
From what I can tell there is no interpolation happening at all. Any ideas?
Well I found this: http://www.mail-archive.com/mono-devel-list#lists.ximian.com/msg18099.html
I guess the underlying code of Mono's drawing routines are at fault. YAY! Now I get to write my own downscaler.
See:
High quality image re-sampling in Mono/C#/ASP.NET
http://www.toptensoftware.com/blog/posts/17/high-quality-image-resampling-in-monolinux

Categories

Resources