Problems with loading a BitmapPalette from image file - c#

I want to load a palette from a bitmap file I have created. The file is 256 px wide and 1 px high.
I use
BitmapImage image = new BitmapImage();
image.BeginInit();
image.UriSource = new Uri("image.bmp", UriKind.RelativeOrAbsolute);
image.EndInit();
myPalette = new BitmapPalette(image, 256);
The strange thing is, that the Count property of myPalette.Colors is only 244!
Is there something wrong with my code?

There is nothing wrong with your code - BitmapPalette returns up to the number of colors specified. From here: http://msdn.microsoft.com/en-us/library/system.windows.media.imaging.bitmappalette.aspx
Initializes a new instance of the BitmapPalette class based on the
specified BitmapSource. The new BitmapPalette is limited to a
specified maximum color count.
I created a test bitmap with the same dimensions and a single color, and myPalette.Colors.Count returned 1.
Note however that you may actually want to use Bitmap from System.Drawing, much simpler and this returns the full palette list (256) even if they are all the same:
var test = new Bitmap("image.bmp");
Console.WriteLine(test.Palette.Entries.Length);
With the same test as above, that returns 256 entries

Related

Resize Bitmap without losing original ratio on C#

This code basically takes an image and crops it according to a detected image (in this case, it detects a decentralized fingerprint, and returns a new Bitmap with the fingerprint centered and cropped).
It turns out that, depending on the image, each resulting Bitmap will have a different size (for example, my test is returning a 425x448 Bitmap, since the identified image has that size), when in fact I need the image to return with a specific size (512x512).
I've already tried to change all the height and width variables of the code, but none satisfy this desired condition. Either it creates a Bitmap with the size of 512x512 and stretches the original image (violating the original ratio), or it creates a 512x512 Bitmap with the cropped image but with a black border on the right and bottom sides.
Any hints of what can be changed or included in the code?
Edit: More clearly, I need to create a 512x512 canvas for the 425x448 image without changing the size or dimensions of the image (it should be 425x448 inside a 512x512 canvas).
private byte[] GetAndCropImage(byte[] image, IEnumerable<YoloItem> yoloItems)
{
byte[] imageRet = null;
var topYoloItem = yoloItems?.Where(x => x.Confidence >= 0.30).OrderByDescending(x => x.Confidence).First();
MemoryStream ms = new MemoryStream(image);
Bitmap src = new Bitmap(ms);
Rectangle cropRect = new Rectangle(topYoloItem.X, topYoloItem.Y, topYoloItem.Width, topYoloItem.Height);
Bitmap target = new Bitmap(cropRect.Width, cropRect.Height);
using (Graphics g = Graphics.FromImage(target))
{
g.DrawImage(src, new Rectangle(0, 0, target.Width, target.Height),
cropRect,
GraphicsUnit.Pixel);
}
target.SetResolution(512, 512);
ImageConverter converter = new ImageConverter();
imageRet = (byte[])converter.ConvertTo(target, typeof(byte[]));
return imageRet;
}

Grayscale 8BPP bitmap for SharpDX.Direct2D1.Bitmap

I am currently making a 2D application using SharpDX.Direct2D1.
Here is my setup for my 2D deviceContext that works correctly.
m_swapchaindesc = New SwapChainDescription()
With m_swapchaindesc
.BufferCount = 2
.ModeDescription =
New ModeDescription(control.Width, control.Height,
New Rational(60, 1), Format.R8G8B8A8_UNorm)
.IsWindowed = True
.OutputHandle = control.Handle
.SampleDescription = New SampleDescription(1, 0)
.SwapEffect = SwapEffect.Discard
.Usage = Usage.RenderTargetOutput
End With
SharpDX.Direct3D11.Device.CreateWithSwapChain(DriverType.Hardware, DeviceCreationFlags.BgraSupport Or DeviceCreationFlags.Debug, m_swapchaindesc, m_device, m_swapChain)
Dim dxgiDevice As SharpDX.DXGI.Device = m_device.QueryInterface(Of SharpDX.DXGI.Device)()
m_2dDevice = New SharpDX.Direct2D1.Device(dxgiDevice)
m_d2dContext = New SharpDX.Direct2D1.DeviceContext(m_2dDevice, SharpDX.Direct2D1.DeviceContextOptions.None)
m_properties = New BitmapProperties(New SharpDX.Direct2D1.PixelFormat(SharpDX.DXGI.Format.R8G8B8A8_UNorm, SharpDX.Direct2D1.AlphaMode.Premultiplied), 96, 96)
m_backBuffer = m_swapChain.GetBackBuffer(Of Surface)(0)
m_2dTarget = New SharpDX.Direct2D1.Bitmap(m_d2dContext, m_backBuffer, m_properties)
m_d2dContext.Target = m_2dTarget
My question : I want to draw 8bpp bitmap on a grayscale to my deviceContext, which means I don't need it to be 32bits per pixel which takes a loooot of memory. I would like for it to be only 8 bits for memory purpose. The problem when I do so, which means setting up my BitmapProperties, there is only a Format.R8_Unorm Or Format.A8_Unorm that seems interesting since I guess they only have 8bpp. However, when I put this format and I am trying to instantiate my SharpDX.Direct2D1.Bitmap, it returns an error message that says WRONGPIXELFORMAT.
I realised that only Format.R8G8B8A8_Unorm seems to work when creating bitmap, which I don't need.
Have any of you worked with 8bpp grayscale bitmap on SharpDX ?
You cannot use 8BPP bitmaps with SharpDX (DirectX). This is not a supported format by DirectX. To be able to display a grayscale image, You have to set all values of yours rgb to the same one, or apply a shaders to your image that converts all colors to gray.

Unsupported Pixel Format of source or template image. AForge Imaging

I am getting the following Exception at ProcessImage(bitmap1, bitmap2);
Unsupported Pixel Format of source or template image
and this is my code:
public static double FindComparisonRatioBetweenImages(
System.Drawing.Image one, System.Drawing.Image two)
{
Bitmap bitmap1 = new Bitmap(one);
Bitmap bitmap2 = new Bitmap(two);
ExhaustiveTemplateMatching tm = new ExhaustiveTemplateMatching(0);
TemplateMatch[] matchings = null;
matchings = tm.ProcessImage(bitmap1, bitmap2); // Exception occurs here!
return matchings[0].Similarity;
}
I have also passed managedImage from the below code into the method, but it still gives error:
UnmanagedImage unmanagedImageA = UnmanagedImage.FromManagedImage(bitmap1);
Bitmap managedImageA = unmanagedImageA.ToManagedImage();
UnmanagedImage unmanagedImageB = UnmanagedImage.FromManagedImage(bitmap2);
Bitmap managedImageB = unmanagedImageB.ToManagedImage();
I have passed Images randomly from my computer, they all give exception.
I have passed Blank Image edited in paint into the method,it still give exception.
Also checked, jpeg, png, bmp formats, nothing work.
Try ExhaustiveTemplateMatching:
The class implements exhaustive template matching algorithm, which performs complete scan of source image, comparing each pixel with corresponding pixel of template.
The class processes only grayscale 8 bpp and color 24 bpp images.
So, those are the image formats you must use.
As requested, to convert to a specific pixel format, you can do this:
public static Bitmap ConvertToFormat(this Image image, PixelFormat format)
{
Bitmap copy = new Bitmap(image.Width, image.Height, format);
using (Graphics gr = Graphics.FromImage(copy))
{
gr.DrawImage(image, new Rectangle(0, 0, copy.Width, copy.Height));
}
return copy;
}
The one you would use is System.Drawing.Imaging.PixelFormat.Format24bppRgb.

Bitmap.Clone creates an image that is 4x Larger in output size than original

So, I have an app that takes an original image, gets the new new cropped region, and then saves the cropped version of the image as a new file. It works perfectly with one major drawback. The new image is, on average, 4x larger than the original image. In my test, I have a photograph that has a size on disk of ~4.5MB, and the cropped version (which is properly cropped and looks fine) is ~21MB on disk. The code is as follows:
var originalImage = new Bitmap(imagePath);
var fWidth = originalImage.PhysicalDimension.Width;
var fHeight = originalImage.PhysicalDimension.Height;
float calculatedWidth = GetCroppedWidth();
float calculatedHeight = GetCroppedHeight();
//Draw the image by centering the cropped region on the original
var heightOffset = (fHeight - calculatedHeight) / 2;
var widthOffset = (fWidth - calculatedWidth) / 2;
var sourceRectF = new RectangleF(widthOffset, heightOffset, calculatedWidth, calculatedHeight);
var croppedImage = originalImage.Clone(sourceRectF, originalImage.PixelFormat);
//Save the image
croppedImage.Save(croppedFileName);
It sounds like the image you are loading is some other format than BMP (e.g. PNG or JPG).
Use another overload of Bitmap.Save that specified an ImageFormat
Look at the Bitmap.Save overload that lets you choose the output format.
By default, it is Bmp i guess, which has no compression.
so, in your case use
croppedImage.Save(croppedFileName, originalImage.RawFormat);

Image resizing - sometimes very poor quality?

I'm resizing some images to the screen resolution of the user; if the aspect ratio is wrong, the image should be cut.
My code looks like this:
protected void ConvertToBitmap(string filename)
{
var origImg = System.Drawing.Image.FromFile(filename);
var widthDivisor = (double)origImg.Width / (double)System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width;
var heightDivisor = (double)origImg.Height / (double)System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height;
int newWidth, newHeight;
if (widthDivisor < heightDivisor)
{
newWidth = (int)((double)origImg.Width / widthDivisor);
newHeight = (int)((double)origImg.Height / widthDivisor);
}
else
{
newWidth = (int)((double)origImg.Width / heightDivisor);
newHeight = (int)((double)origImg.Height / heightDivisor);
}
var newImg = origImg.GetThumbnailImage(newWidth, newHeight, null, IntPtr.Zero);
newImg.Save(this.GetBitmapPath(filename), System.Drawing.Imaging.ImageFormat.Bmp);
}
In most cases, this works fine. But for some images, the result has an extremely poor quality. It looks like the would have been resized to something very small (thumbnail size) and enlarged again.. But the resolution of the image is correct. What can I do?
Example orig image:
alt text http://img523.imageshack.us/img523/1430/naturaerowoods.jpg
Example resized image:
Note: I have a WPF application but I use the WinForms function for resizing because it's easier and because I already need a reference to System.Windows.Forms for a tray icon.
Change the last two lines of your method to this:
var newImg = new Bitmap(newWidth, newHeight);
Graphics g = Graphics.FromImage(newImg);
g.DrawImage(origImg, new Rectangle(0,0,newWidth,newHeight));
newImg.Save(this.GetBitmapPath(filename), System.Drawing.Imaging.ImageFormat.Bmp);
g.Dispose();
I cannot peek into the .NET source at the moment, but most likely the problem is in the Image.GetThumbnailImage method. Even MSDN says that "it works well when the requested thumbnail image has a size of about 120 x 120 pixels, but it you request a large thumbnail image (for example, 300 x 300) from an Image that has an embedded thumbnail, there could be a noticeable loss of quality in the thumbnail image". For true resizing (i.e. not thumbnailing), you should use the Graphics.DrawImage method. You may also need to play with the Graphics.InterpolationMode to get a better quality if needed.
If you're not creating a thumbnail, using a method called GetThumbnailImage probably isn't a good idea...
For other options, have a look at this CodeProject article. In particular, it creates a new image, creates a Graphics for it and sets the interpolation mode to HighQualityBicubic and draws the original image onto the graphics. Worth a try, at least.
As indicated on MSDN, GetThumbnailImage() is not designed to do arbitrary image scaling. Anything over 120x120 should be scaled manually. Try this instead:
using(var newImg = new Bitmap(origImg, newWidth, newHeight))
{
newImg.Save(this.GetBitmapPath(filename), System.Drawing.Imaging.ImageFormat.Bmp);
}
Edit
As a point of clarification, this overload of the Bitmap constructor calls Graphics.DrawImage, though you do not have any control over the interpolation.
instead of this code:
newImg.Save(this.GetBitmapPath(filename), System.Drawing.Imaging.ImageFormat.Bmp);
use this one :
System.Drawing.Imaging.ImageCodecInfo[] info = System.Drawing.Imaging.ImageCodecInfo.GetImageEncoders();
System.Drawing.Imaging.EncoderParameters param = new System.Drawing.Imaging.EncoderParameters(1);
param.Param[0] = new System.Drawing.Imaging.EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 100L);
newImg.Save(dest_img, info[1], param);
For examples, the original image is JPG and the resized image is PNG. Are you converting between formats on purpose? Switching between different lossey compression schemes can cause quality loss.
Are you increasing or decreasing the size of the image when you resize it? If you are creating a larger image from a smaller one, this sort of degradation is to be expected.
Images will definitely be degraded if you enlarge them.
Some camera's put a resized thumbnail into the file itself presumably for preview purposes on the device itself.
The GetThumbnail method actually gets this Thumbnail image which is embedded within the image file instead of getting the higher res method.
The easy solution is to trick .Net into throwing away that thumbnail information before doing your resize or other operation. like so....
img.RotateFlip(System.Drawing.RotateFlipType.Rotate180FlipX);
//removes thumbnails from digital camera shots
img.RotateFlip(System.Drawing.RotateFlipType.Rotate180FlipX);
If you are attempting to resize constraining proportions I wrote an extension method on System.Drawing.Image that you might find handy.
/// <summary>
///
/// </summary>
/// <param name="img"></param>
/// <param name="size">Size of the constraining proportion</param>
/// <param name="constrainOnWidth"></param>
/// <returns></returns>
public static System.Drawing.Image ResizeConstrainProportions(this System.Drawing.Image img,
int size, bool constrainOnWidth, bool dontResizeIfSmaller)
{
if (dontResizeIfSmaller && (img.Width < size))
return img;
img.RotateFlip(System.Drawing.RotateFlipType.Rotate180FlipX);
img.RotateFlip(System.Drawing.RotateFlipType.Rotate180FlipX);
float ratio = 0;
ratio = (float)img.Width / (float)img.Height;
int height, width = 0;
if (constrainOnWidth)
{
height = (int)(size / ratio);
width = size;
}
else
{
width = (int)(size * ratio);
height = size;
}
return img.GetThumbnailImage(width, height, null, (new System.IntPtr(0)));
}
This is going to vary widely based on the following factors:
How closely the destination resolution matches a "natural" scale of the original resolution
The source image color depth
The image type(s) - some are more lossy than others

Categories

Resources