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.
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 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);
}
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);
}
}
The problem is that I can't select minimalization filter for Texture2D scaling. There is MinFilter
GraphicsDevice.SamplerStates[0].MinFilter
But it's not working. When I try to assign some filter it just changes back to the Linear.
Is there a way to implement my own filter or how to select one of the available?
I read an article on Shawn Hargreaves' blog a while back that said, "SpriteBatch will automatically set what it needs for drawing in 2D...", which includes setting the MinFilter to Linear.
So, you could try what this article says to do and set your SpriteSortMode to Immediate. Then after your SpriteBatch.Begin call, you can set the MinFilter to whatever you want, and it should retain that setting when it draws the sprite.
I have one answer to my own question and i think it will be better to post a new answer for readability.
private Texture2D Scale(GraphicsDevice gd, Texture2D texture, float scale)
{
int sourceWidth = texture.Width;
int sourceHeight = texture.Height;
int destWidth = (int)(sourceWidth * scale);
int destHeight = (int)(sourceHeight * scale);
//convert texture into bitmap
byte[] textureData = new byte[4 * sourceWidth * sourceHeight];
texture.GetData<byte>(textureData);
System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(sourceWidth, sourceHeight, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
System.Drawing.Imaging.BitmapData bmpData = bmp.LockBits(new System.Drawing.Rectangle(0, 0, sourceWidth, sourceHeight), System.Drawing.Imaging.ImageLockMode.WriteOnly,
System.Drawing.Imaging.PixelFormat.Format32bppArgb);
IntPtr safePtr = bmpData.Scan0;
System.Runtime.InteropServices.Marshal.Copy(textureData, 0, safePtr, textureData.Length);
bmp.UnlockBits(bmpData);
//output bitmap
System.Drawing.Image outputImage = new System.Drawing.Bitmap(destWidth, destHeight, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
System.Drawing.Graphics grPhoto = System.Drawing.Graphics.FromImage(outputImage);
grPhoto.InterpolationMode = (System.Drawing.Drawing2D.InterpolationMode)(interpolationMode);
grPhoto.DrawImage((System.Drawing.Image)bmp, new System.Drawing.Rectangle(0, 0, destWidth, destHeight),
new System.Drawing.Rectangle(0, 0, sourceWidth, sourceHeight), System.Drawing.GraphicsUnit.Pixel);
grPhoto.Dispose();
textureData = new byte[4 * sourceWidth * sourceHeight];
MemoryStream ms = new MemoryStream();
((System.Drawing.Bitmap)outputImage).Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);
ms.Seek(0, SeekOrigin.Begin);
Texture2D result = Texture2D.FromFile(gd, ms);
ms.Dispose();
return result;
}
This metod runs in about 20ms - 100ms depending on interpolation mode, maybe someone will find it usefull. Is there a way to optimize it? I dont need 32bit support but there seems to be no way to set it to 24. I found different sizing algorithms
http://www.codeproject.com/KB/GDI-plus/imgresizoutperfgdiplus.aspx
but they are slow.
Is there a way to implement these methods using speed of graphic card?
The best approach to minification is to create mipmaps for the textures as the hardware and the directx pipeline is optimized to use mipmaps in the best way possible depending on the final drawing size of the texture.
Here's a quote from MSDN on mipmaps
The most popular approach to
minification is to create mipmaps for
each texture. A mipmap is a pre-shrunk
texture, normally half the size of the
original. The mipmap itself then gets
mipmapped, and this process continues
until a 1x1 texture is created. This
is the final mipmap for the texture.
You can think of mipmaps as a chain,
starting with the original texture and
becoming smaller and smaller until the
1 texel texture is reached. When
minification is needed, first the
appropriate mipmapped texture is
chosen, then that mipmap is applied to
the object, with real-time texture
filtering if needed. The default
Texture processor for the Content
Pipeline has an option to generate
mipmaps automatically.
You can enable automatic mipmap generation using the content pipeline or Texture.GenerateMipMaps to generate mipmaps at runtime for a render target texture.
The best way I can see, when using spriteBatch (2D mode) is to use one of the longer spriteBatch.Begin() overloads, specifically the one here works for me:
spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, SamplerState.PointClamp, DepthStencilState.Default,RasterizerState.CullNone);
the important part is the SamplerState.PointClamp, which tells all draw() calls until the next spriteBatch.end() to use point- sampling for both min and max. There are several other options in SamplerState, which I'm sure intelisense can describe better than I.
for reference:
http://msdn.microsoft.com/en-us/library/ff433699.aspx
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.