I used the below code to clone a bitmap image without locking the original file. But i am facing an issue that cloned image (.Gif) is not the same as the original image. Especially, the color of the cloned image is not proper.
Am I doing anything wrong? Is any better way to have Image in memory and original file deleted from hard disk?
Code:
private Bitmap CloneImage(Bitmap src)
{
if (src == null)
return src;
Bitmap bitmap = new Bitmap(src.Size.Width, src.Size.Height, src.PixelFormat);
System.Drawing.Rectangle bounds = new System.Drawing.Rectangle(0, 0, src.Width, src.Height);
System.Drawing.Imaging.BitmapData bmpData = src.LockBits(bounds, System.Drawing.Imaging.ImageLockMode.ReadWrite, src.PixelFormat);
System.Drawing.Imaging.BitmapData newBmpData = bitmap.LockBits(bounds, System.Drawing.Imaging.ImageLockMode.ReadWrite, src.PixelFormat);
IntPtr bPtr = bmpData.Scan0;
IntPtr nbPtr = newBmpData.Scan0;
int bytes = Math.Abs(bmpData.Stride) * src.Height;
byte[] rgbValues = new byte[bytes];
System.Runtime.InteropServices.Marshal.Copy(bPtr, rgbValues, 0, bytes);
System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, nbPtr, bytes);
bitmap.UnlockBits(newBmpData);
src.UnlockBits(bmpData);
return bitmap;
}
Original Image:
Cloned image:
This looks like a palette issue. The individual pixels are at the right location, so the memcpy code is probably right.
Either also copy the palette, or use a 24 or 32 bit pixel format and use Graphics.FromImage to blit the source image onto the target bitmap. Then you can save as PNG which is probably going to be a smaller file anyway.
It is easier (less code) to clone the image by saving it to a MemoryStream. Then you can load it from the MemoryStream and you will have your cloned Bitmap and you won't have to mess with pixel formats.
According to this SO answer, what you are doing here should be as hard as,
private Bitmap CloneImage(Bitmap src)
{
return new Bitmap(src);
}
Related
sorry if this question is silly, i'm just starting with C#
I have an image in disk which I know the path. The image is 20000x10000 aprox in size and around 400MB in size (i know this looking at the image)
I need to load it in the code and resize it since the progam dies if I try to put in a picture box 400m of image, but if I do
Bitmap b0 = new Bitmap(pathImage);
int newWidth = (int)(b0.Width * escala);
int newHeight = (int)(b0.Height * escala);
// Convert other formats (including CMYK) to RGB.
Bitmap newImage = new Bitmap(newWidth, newHeight, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
// Draws the image in the specified size with quality mode set to HighQuality
using (Graphics graphics = Graphics.FromImage(newImage))
{
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.DrawImage(b0, 0, 0, newWidth, newHeight);
}
But when I do Bitmap b0 = new Bitmap(pathImage); it just dies since 20000x10000x32 it's too much for my poor memory.
The problem is that if not specified, c# just uses as pixel format Format32bppArgb but I want Format1bppIndexed, and the only way i've found to change the format is with the constructor
Bitmap(
int width,
int height,
int stride,
PixelFormat format,
IntPtr scan0
)
But for this constructor I need to know the size of the image, which in the code I don't know because it dies before the code can get it and know the size...
I'm lost. Any ideas?
EDIT:
Doing this
int width;
int height;
Image tif;
using (FileStream file = new FileStream(rutaImagenEntrada, FileMode.Open, FileAccess.ReadWrite))
{
using ( tif = Image.FromStream(stream: file,
useEmbeddedColorManagement: false,
validateImageData: false))
{
width = (int)tif.PhysicalDimension.Width;
height = (int)tif.PhysicalDimension.Height;
}
file.Close();
}
I get the size of the image I cannot load, and can create a bitmap with the pixelformat and size that i wanted, BUT everyway I try to upload it doesn't work, it still crashes on me.
I've tried using graphics .draw(tiff...) and tiff.getThumbnail(new size...) and it just dies on me.
It targets x86 and the .Net 3.5 since those are the requirements of the client...
Any ideas?
If FileStream works to open the file, you might want to give some of the other constructors of Bitmap a try, in particular Bitmap(Stream).
If that doesn't help, you might try loading the raw bmp data, creating a Bitmap with the format you want and copying the data into the Bitmap manually (I'm not familiar with C# Bitmaps, but there should be some way of accessing the data).
Hope this helps.
I'm trying to take a writeablebitmap that constantly updates and render it into an Image however only the top of the image displays and the middle and bottom 2/3's are black. I think it might have something to do with PixelFormat as the writeablebitmap is bgr24 and the image is rgb24. This is what I'm currently doing.
int bufferSize = videoRenderer.VideoWidth * videoRenderer.VideoHeight;
byte[] frameBuffer = new byte[bufferSize];
Marshal.Copy(videoRenderer.Bitmap.BackBuffer, frameBuffer, 0, bufferSize);
using (Bitmap frame = new Bitmap(videoRenderer.VideoWidth, videoRenderer.VideoHeight, System.Drawing.Imaging.PixelFormat.Format24bppRgb))
{
System.Drawing.Rectangle rect = new System.Drawing.Rectangle(0, 0, videoRenderer.VideoWidth, videoRenderer.VideoHeight);
BitmapData bmpData = frame.LockBits(rect, ImageLockMode.ReadWrite, frame.PixelFormat);
Marshal.Copy(frameBuffer, 0, bmpData.Scan0, bufferSize);
frame.UnlockBits(bmpData);
IntPtr hBitmap = frame.GetHbitmap();
source = Imaging.CreateBitmapSourceFromHBitmap(hBitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
DeleteObject(hBitmap);
}
Does something look horribly wrong, or is it most likely the pixelformat. And if the pixelformat, how would one go through each pixel in c# to swap the blue's and red's?
It can't be just the pixel format in this case (it wouldn't cause 1/3 of the image to be rendered and the rest to be black). The difference between BGR24 and RGB24 is just a color swap.
In your case it's probably has to do with a difference in stride between the bitmap and the video source.
And as far as converting from BGR24 to RGB24 you could do it manually by locking the writable bitmap and looping over each pixel and swapping the components but that is not going to have good performance.
A much better approach would be to use the FormatConvertedBitmap class.
I have a small piece of C# code that uses a Kinect to detect up to 4 glyphs and draws and polygon between them on a canvas, as seen here:
I've tried to follow along to this in order to implement 2D augmented reality and project an image within the created polygon. I've read in a source image and tried to apply the BackwardQuadrilateralTransformation to it but can't seem to display the transformed image. I am probably using the wrong method but I have tried to convert the image and paint it onto a canvas with no luck. I'm not sure if I'm just massively misunderstanding the method and maybe it isn't possible, any help would be greatly appreciated. I can supply more sample code if required.
private void GlyphBackQuad(List<IntPoint> quadpoints)
{
Bitmap srcImage = new Bitmap( // my sample image filepath );
UnmanagedImage sourceImage = UnmanagedImage.FromManagedImage(srcImage);
BackwardQuadrilateralTransformation filter = new BackwardQuadrilateralTransformation(sourceImage, quadpoints);
filter.Apply(sourceImage);
Bitmap bmp = sourceImage.ToManagedImage();
ImageBrush ib = new ImageBrush();
ib.ImageSource = ConvertDrawingImage2MediaImageSource(bmp);
PolyCanvas.Background = ib;
}
After a further play around with the code I think I have developed a partial solution, it still needs some work but hopefully this is more helpful for people to read/debug.
private void GlyphBackQuad(List<IntPoint> quadpoints, Bitmap bmp)
{
// Read in bitmap source image and clone it to the same format as destination
Bitmap srcImage = AForge.Imaging.Image.Clone(new Bitmap( // my sample filepath), System.Drawing.Imaging.PixelFormat.Format24bppRgb);
System.Drawing.Imaging.BitmapData bitmapData = bmp.LockBits(new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height), System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
// Convert to unmanaged image
UnmanagedImage unmanagedImage = new UnmanagedImage( bitmapData );
// Filter
BackwardQuadrilateralTransformation filter = new BackwardQuadrilateralTransformation(srcImage, quadpoints);
filter.ApplyInPlace(unmanagedImage);
// Convert back to managed image and save
Bitmap managedImage = unmanagedImage.ToManagedImage();
managedImage.Save( // my save filepath, System.Drawing.Imaging.ImageFormat.Png);
}
I'm using this code to capture the screen:
public Bitmap CaptureWindow(IntPtr handle)
{
// get te hDC of the target window
IntPtr hdcSrc = User32.GetWindowDC(handle);
// get the size
User32.RECT windowRect = new User32.RECT();
User32.GetWindowRect(handle, ref windowRect);
int width = windowRect.right - windowRect.left;
int height = windowRect.bottom - windowRect.top;
// create a device context we can copy to
IntPtr hdcDest = GDI32.CreateCompatibleDC(hdcSrc);
// create a bitmap we can copy it to,
// using GetDeviceCaps to get the width/height
IntPtr hBitmap = GDI32.CreateCompatibleBitmap(hdcSrc, width, height);
// select the bitmap object
IntPtr hOld = GDI32.SelectObject(hdcDest, hBitmap);
// bitblt over
GDI32.BitBlt(hdcDest, 0, 0, width, height, hdcSrc, 0, 0, GDI32.SRCCOPY);
// restore selection
GDI32.SelectObject(hdcDest, hOld);
// clean up
GDI32.DeleteDC(hdcDest);
User32.ReleaseDC(handle, hdcSrc);
// get a .NET image object for it
Bitmap img = Image.FromHbitmap(hBitmap);
// free up the Bitmap object
GDI32.DeleteObject(hBitmap);
return img;
}
I then want to convert the bitmap to 256 colors (8 bit). I tried this code but get an error about not being able to create an Image from an indexed bitmap format:
Bitmap img8bit = new Bitmap(img.Width,img.Height,
System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
Graphics g = Graphics.FromImage(img8bit);
g.DrawImage(img,new Point(0,0));
I did see some examples to convert bitmaps between different formats, but in my case I'm looking for the best way to do this while capturing from the screen. For example, if there is a method that will work better by creating an 8-bit bitmap to begin with and then blit the screen to that, that would be preferred over caputring screen to comptible bitmap first and then converting it. Unless it's better to capture then convert anyway.
I have a program written in C++ using Borland Builder 6.0 VCL, and I'm trying to memic that. In that case it is a simple matter of setting the pixel format for VCL's TBitmap object. I notice Bitmap.PixelFormat is read-only in .NET, ugh.
Update: In my case I don't think the answer is as complex as some other usage that requires figuring out the best palette entries, because Graphics.GetHalftonePalette using the screen DC should be fine, since my original bitmap comes from the screen, not just any random bitmap that might come from a file/email/download/etc. I beleive there is something that can be done with maybe 20 lines of code that involves DIBs and GetHalftonePalette -- just can't find it yet.
Converting a full color bitmap to 8bpp is a difficult operation. It requires creating a histogram of all the colors in the image and creating a palette that contains an optimized set of colors that best map to the original colors. Then using a technique like dithering or error diffusion to replace the pixels whose colors don't have an exact match with the palette.
This is best left to a professional graphics library, something like ImageTools. There is one cheap way that can be tricked in the .NET framework. You can use the GIF encoder, a file format that has 256 colors. The result isn't the greatest, it uses dithering and that can be pretty visible sometimes. Then again, if you really cared about image quality then you wouldn't use 8bpp anyway.
public static Bitmap ConvertTo8bpp(Image img) {
var ms = new System.IO.MemoryStream(); // Don't use using!!!
img.Save(ms, System.Drawing.Imaging.ImageFormat.Gif);
ms.Position = 0;
return new Bitmap(ms);
}
Capture the screen using a regular PixelFormat and then use Bitmap.Clone() to convert it to an optimized 256 indexed color like this:
public static Bitmap CaptureScreen256()
{
Rectangle bounds = SystemInformation.VirtualScreen;
using (Bitmap Temp = new Bitmap(bounds.Width, bounds.Height, PixelFormat.Format24bppRgb))
{
using (Graphics g = Graphics.FromImage(Temp))
{
g.CopyFromScreen(0, 0, 0, 0, Temp.Size);
}
return Temp.Clone(new Rectangle(0, 0, bounds.Width, bounds.Height), PixelFormat.Format8bppIndexed);
}
}
I am successfully drawn images from their raw pixel data.(only 8 bit images).
here is the code for doing the same thing.
PixelFormat format = PixelFormat.Format8bppIndexed;
Bitmap bmp = new Bitmap(Img_Width, Img_Height, format);
Rectangle rect = new Rectangle(0, 0, Img_Width, Img_Height);
BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadWrite, format);
Marshal.Copy(rawPixel, 0, bmpData.Scan0, rawPixel.Length);
bmp.UnlockBits(bmpData);
Now as you all know PixelFormat.format16bppGrayscale is not supported by c# 2.0 GDI+.
I googled and got 3.0/3.5 framework support this.
So i installed both.
The class which is support is System.windows.media.PixelFormats.
PixelFormats.Gray16
Now my problem is how to create a bitmap and get a image for display by passing this parameter.
i got something BitmapSource class there but i am very new in C#3.0.
Please help me.
Try this:
private static Bitmap changePixelFormat(Bitmap input, PixelFormat format)
{
Bitmap retval=new Bitmap(input.Width, input.Height, format);
retval.SetResolution(input.HorizontalResolution, input.VerticalResolution);
Graphics g = Graphics.FromImage(retval);
g.DrawImage(input, 0, 0);
g.Dispose();
return retval;
}
Take a look at the Greyscale filters in AForge.net. You can find source here.
EDIT:
As a note on that source I linked, it is using an 'old' version of AForge.NET, but the concepts are the same.