I have a uEye camera and I take snapshots of images at a 1000ms interval and I want to render them in a WPF Image Control like so
Bitmap MyBitmap;
// get geometry of uEye image buffer
int width = 0, height = 0, bitspp = 0, pitch = 0, bytespp = 0;
long imagesize = 0;
m_uEye.InquireImageMem(m_pCurMem, GetImageID(m_pCurMem), ref width, ref height, ref bitspp, ref pitch);
bytespp = (bitspp + 1) / 8;
imagesize = width * height * bytespp; // image size in bytes
// bulit a system bitmap
MyBitmap = new Bitmap(width, height, PixelFormat.Format24bppRgb);
// fill the system bitmap with the image data from the uEye SDK buffer
BitmapData bd = MyBitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
m_uEye.CopyImageMem(m_pCurMem, GetImageID(m_pCurMem), bd.Scan0);
MyBitmap.UnlockBits(bd);
I am trying to put these bitmaps in to an Image control at the rate of 1 second. How can I get Bitmap to appear in the Image control and disposing them as soon as I'm done to leave minimal memory footprint to be a good little programmer :) ?
Here the way we do (for me it works at 200fps without loading CPU (about 5%)):
private WriteableBitmap PrepareForRendering(VideoBuffer videoBuffer) {
PixelFormat pixelFormat;
if (videoBuffer.pixelFormat == PixFrmt.rgb24) {
pixelFormat = PixelFormats.Rgb24;
} else if (videoBuffer.pixelFormat == PixFrmt.bgra32) {
pixelFormat = PixelFormats.Bgra32;
} else if (videoBuffer.pixelFormat == PixFrmt.bgr24) {
pixelFormat = PixelFormats.Bgr24;
} else {
throw new Exception("unsupported pixel format");
}
var bitmap = new WriteableBitmap(
videoBuffer.width, videoBuffer.height,
96, 96,
pixelFormat, null
);
_imgVIew.Source = bitmap;
return bitmap;
}
private void DrawFrame(WriteableBitmap bitmap, VideoBuffer videoBuffer, double averangeFps) {
VerifyAccess();
if (isPaused) {
return;
}
bitmap.Lock();
try {
using (var ptr = videoBuffer.Lock()) {
bitmap.WritePixels(
new Int32Rect(0, 0, videoBuffer.width, videoBuffer.height),
ptr.value, videoBuffer.size, videoBuffer.stride,
0, 0
);
}
} finally {
bitmap.Unlock();
}
fpsCaption.Text = averangeFps.ToString("F1");
}
Related
I am trying to convert every frame I get from my sample grabber into a bitmap however it does not seem to work.
I am using the SampleCB as follows:
int ISampleGrabberCB.SampleCB(double SampleTime, IMediaSample sample)
{
try
{
int lengthOfFrame = sample.GetActualDataLength();
IntPtr buffer;
if (sample.GetPointer(out buffer) == 0 && lengthOfFrame > 0)
{
Bitmap bitmapOfFrame = new Bitmap(width, height, capturePitch, PixelFormat.Format24bppRgb, buffer);
Graphics g = Graphics.FromImage(bitmapOfFrame);
Pen framePen = new Pen(Color.Black);
g.DrawLine(framePen, 30, 30, 50, 50);
g.Flush();
}
CopyMemory(imageBuffer, buffer, lengthOfFrame);
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
Marshal.ReleaseComObject(sample);
return 0;
}
I am drawing a small graphic on it as a tester and it does not seem to work. From what I believe this should be adding a small line to each frame therefore updating my preview with the line.
I can give additional code if needed (e.g How i set up my graph and connect my ISampleGrabber)
Edited with what i think Dee Mon means:
int ISampleGrabberCB.SampleCB(double SampleTime, IMediaSample sample)
{
try
{
int lengthOfFrame = sample.GetActualDataLength();
IntPtr buffer;
BitmapData bitmapData = new BitmapData();
if (sample.GetPointer(out buffer) == 0 && lengthOfFrame > 0)
{
Bitmap bitmapOfFrame = new Bitmap(width, height, capturePitch, PixelFormat.Format24bppRgb, buffer);
Graphics g = Graphics.FromImage(bitmapOfFrame);
Pen framePen = new Pen(Color.Black);
g.DrawLine(framePen, 30, 30, 50, 50);
g.Flush();
Rectangle rect = new Rectangle(0, 0, bitmapOfFrame.Width, bitmapOfFrame.Height);
bitmapData = bitmapOfFrame.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
IntPtr bitmapPointer = bitmapData.Scan0;
CopyMemory(bitmapPointer, buffer, lengthOfFrame);
BitmapOfFrame.UnlockData(bitmapData);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
Marshal.ReleaseComObject(sample);
return 0;
}
When you create a Bitmap it copies data to its own internal buffer and all the drawing goes in that buffer, not in yours. Use Bitmap.LockBits and BitmapData class to get its contents after you draw your stuff in the bitmap.
I have an image width/height/stride and buffer.
How do I convert this information to a System.Drawing.Bitmap? Can I get the original image back if I have these 4 things?
There is a Bitmap constructor overload, which requires everything you have (plus PixelFormat):
public Bitmap(int width, int height, int stride, PixelFormat format, IntPtr scan0);
This might work (if args.Buffer is an array of blittable type, like byte for example):
Bitmap bitmap;
var gch = System.Runtime.InteropServices.GCHandle.Alloc(args.Buffer, GCHandleType.Pinned);
try
{
bitmap = new Bitmap(
args.Width, args.Height, args.Stride,
System.Drawing.Imaging.PixelFormat.Format24bppRgb,
gch.AddrOfPinnedObject());
}
finally
{
gch.Free();
}
Update:
Probably it's better to copy image bytes to newly created Bitmap manually, because it seems like that constructors doesn't do that, and if byte[] array of image data gets garbage collected all sorts of bad things can happen.
var bitmap = new Bitmap(args.Width, args.Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
var data = bitmap.LockBits(
new Rectangle(0, 0, args.Width, args.Height),
System.Drawing.Imaging.ImageLockMode.WriteOnly,
System.Drawing.Imaging.PixelFormat.Format24bppRgb);
if(data.Stride == args.Stride)
{
Marshal.Copy(args.Buffer, 0, data.Scan0, args.Stride * args.Height);
}
else
{
int arrayOffset = 0;
int imageOffset = 0;
for(int y = 0; y < args.Height; ++y)
{
Marshal.Copy(args.Buffer, arrayOffset, (IntPtr)(((long)data.Scan0) + imageOffset), data.Stride);
arrayOffset += args.Stride;
imageOffset += data.Stride;
}
}
bitmap.UnlockBits(data);
This should work if you have the buffer as byte[], a width and the height + the pixelformat (stride)
public Bitmap CreateBitmapFromRawDataBuffer(int width, int height, PixelFormat imagePixelFormat, byte[] buffer)
{
Size imageSize = new Size(width, height);
Bitmap bitmap = new Bitmap(imageSize.Width, imageSize.Height, imagePixelFormat);
Rectangle wholeBitmap = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
// Lock all bitmap's pixels.
BitmapData bitmapData = bitmap.LockBits(wholeBitmap, ImageLockMode.WriteOnly, imagePixelFormat);
// Copy the buffer into bitmapData.
System.Runtime.InteropServices.Marshal.Copy(buffer, 0, bitmapData.Scan0, buffer.Length);
// Unlock all bitmap's pixels.
bitmap.UnlockBits(bitmapData);
return bitmap;
}
I've found loads of people converting a BitmapSource to a Bitmap, but what about ImageSource to Bitmap? I am making an imaging program and I need to extract bitmaps from the image displayed in the Image element. Does anyone know how to do this?
EDIT 1:
This is a function for converting the BitmapImage to a Bitmap. Remember to set the 'unsafe' option in the compiler preferences.
public static System.Drawing.Bitmap BitmapSourceToBitmap(BitmapSource srs)
{
System.Drawing.Bitmap btm = null;
int width = srs.PixelWidth;
int height = srs.PixelHeight;
int stride = width * ((srs.Format.BitsPerPixel + 7) / 8);
byte[] bits = new byte[height * stride];
srs.CopyPixels(bits, stride, 0);
unsafe
{
fixed (byte* pB = bits)
{
IntPtr ptr = new IntPtr(pB);
btm = new System.Drawing.Bitmap(width, height, stride, System.Drawing.Imaging.PixelFormat.Format1bppIndexed, ptr);
}
}
return btm;
}
Next is now to get a BitmapImage:
RenderTargetBitmap targetBitmap = new RenderTargetBitmap(
(int)inkCanvas1.ActualWidth,
(int)inkCanvas1.ActualHeight,
96d, 96d,
PixelFormats.Default);
targetBitmap.Render(inkCanvas1);
MemoryStream mse = new MemoryStream();
System.Windows.Media.Imaging.BmpBitmapEncoder mem = new BmpBitmapEncoder();
mem.Frames.Add(BitmapFrame.Create(targetBitmap));
mem.Save(mse);
mse.Position = 0;
BitmapImage bi = new BitmapImage();
bi.BeginInit();
bi.StreamSource = mse;
bi.EndInit();
Next is to convert it:
Bitmap b = new Bitmap(BitmapSourceToBitmap(bi));
Actually you don't need to use unsafe code. There's an overload of CopyPixels that accepts an IntPtr:
public static System.Drawing.Bitmap BitmapSourceToBitmap2(BitmapSource srs)
{
int width = srs.PixelWidth;
int height = srs.PixelHeight;
int stride = width * ((srs.Format.BitsPerPixel + 7) / 8);
IntPtr ptr = IntPtr.Zero;
try
{
ptr = Marshal.AllocHGlobal(height * stride);
srs.CopyPixels(new Int32Rect(0, 0, width, height), ptr, height * stride, stride);
using (var btm = new System.Drawing.Bitmap(width, height, stride, System.Drawing.Imaging.PixelFormat.Format1bppIndexed, ptr))
{
// Clone the bitmap so that we can dispose it and
// release the unmanaged memory at ptr
return new System.Drawing.Bitmap(btm);
}
}
finally
{
if (ptr != IntPtr.Zero)
Marshal.FreeHGlobal(ptr);
}
}
That example worked for me:
public static Bitmap ConvertToBitmap(BitmapSource bitmapSource)
{
var width = bitmapSource.PixelWidth;
var height = bitmapSource.PixelHeight;
var stride = width * ((bitmapSource.Format.BitsPerPixel + 7) / 8);
var memoryBlockPointer = Marshal.AllocHGlobal(height * stride);
bitmapSource.CopyPixels(new Int32Rect(0, 0, width, height), memoryBlockPointer, height * stride, stride);
var bitmap = new Bitmap(width, height, stride, PixelFormat.Format32bppPArgb, memoryBlockPointer);
return bitmap;
}
Are your ImageSource not a BitmapSource? If your loading the images from files they should be.
Reply to your comment:
Sounds like they should be BitmapSource then, BitmapSource is a subtype of ImageSource. Cast the ImageSource to BitmapSource and follow one of those blogposts.
You don't need a BitmapSourceToBitmap method at all. Just do the following after creating your memory stream:
mem.Position = 0;
Bitmap b = new Bitmap(mem);
Using C# how can I resize a jpeg image? A code sample would be great.
I'm using this:
public static void ResizeJpg(string path, int nWidth, int nHeight)
{
using (var result = new Bitmap(nWidth, nHeight))
{
using (var input = new Bitmap(path))
{
using (Graphics g = Graphics.FromImage((System.Drawing.Image)result))
{
g.DrawImage(input, 0, 0, nWidth, nHeight);
}
}
var ici = ImageCodecInfo.GetImageEncoders().FirstOrDefault(ie => ie.MimeType == "image/jpeg");
var eps = new EncoderParameters(1);
eps.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 100L);
result.Save(path, ici, eps);
}
}
Good free resize filter and example code.
http://code.google.com/p/zrlabs-yael/
private void MakeResizedImage(string fromFile, string toFile, int maxWidth, int maxHeight)
{
int width;
int height;
using (System.Drawing.Image image = System.Drawing.Image.FromFile(fromFile))
{
DetermineResizeRatio(maxWidth, maxHeight, image.Width, image.Height, out width, out height);
using (System.Drawing.Image thumbnailImage = image.GetThumbnailImage(width, height, new System.Drawing.Image.GetThumbnailImageAbort(ThumbnailCallback), IntPtr.Zero))
{
if (image.Width < thumbnailImage.Width && image.Height < thumbnailImage.Height)
File.Copy(fromFile, toFile);
else
{
ImageCodecInfo ec = GetCodecInfo();
EncoderParameters parms = new EncoderParameters(1);
parms.Param[0] = new EncoderParameter(Encoder.Compression, 40);
ZRLabs.Yael.BasicFilters.ResizeFilter rf = new ZRLabs.Yael.BasicFilters.ResizeFilter();
//rf.KeepAspectRatio = true;
rf.Height = height;
rf.Width = width;
System.Drawing.Image img = rf.ExecuteFilter(System.Drawing.Image.FromFile(fromFile));
img.Save(toFile, ec, parms);
}
}
}
}
C# (or rather: the .NET framework) itself doesn't offer such capability, but it does offer you Bitmap from System.Drawing to easily access the raw pixel data of various picture formats. For the rest, see http://en.wikipedia.org/wiki/Image_scaling
Nice example.
public static Image ResizeImage(Image sourceImage, int maxWidth, int maxHeight)
{
// Determine which ratio is greater, the width or height, and use
// this to calculate the new width and height. Effectually constrains
// the proportions of the resized image to the proportions of the original.
double xRatio = (double)sourceImage.Width / maxWidth;
double yRatio = (double)sourceImage.Height / maxHeight;
double ratioToResizeImage = Math.Max(xRatio, yRatio);
int newWidth = (int)Math.Floor(sourceImage.Width / ratioToResizeImage);
int newHeight = (int)Math.Floor(sourceImage.Height / ratioToResizeImage);
// Create new image canvas -- use maxWidth and maxHeight in this function call if you wish
// to set the exact dimensions of the output image.
Bitmap newImage = new Bitmap(newWidth, newHeight, PixelFormat.Format32bppArgb);
// Render the new image, using a graphic object
using (Graphics newGraphic = Graphics.FromImage(newImage))
{
using (var wrapMode = new ImageAttributes())
{
wrapMode.SetWrapMode(WrapMode.TileFlipXY);
newGraphic.DrawImage(image, destRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, wrapMode);
}
// Set the background color to be transparent (can change this to any color)
newGraphic.Clear(Color.Transparent);
// Set the method of scaling to use -- HighQualityBicubic is said to have the best quality
newGraphic.InterpolationMode = InterpolationMode.HighQualityBicubic;
// Apply the transformation onto the new graphic
Rectangle sourceDimensions = new Rectangle(0, 0, sourceImage.Width, sourceImage.Height);
Rectangle destinationDimensions = new Rectangle(0, 0, newWidth, newHeight);
newGraphic.DrawImage(sourceImage, destinationDimensions, sourceDimensions, GraphicsUnit.Pixel);
}
// Image has been modified by all the references to it's related graphic above. Return changes.
return newImage;
}
Source : http://mattmeisinger.com/resize-image-c-sharp
ImageMagick should be the best way. Easy and reliable.
using (var image = new MagickImage(imgfilebuf))
{
image.Resize(len, len);
image.Strip();
using MemoryStream ms = new MemoryStream();
image.Write(ms);
return ms.ToArray();
}
I need to convert bitonal (black and white) TIFF files into another format for display by a web browser, currently we're using JPGs, but the format isn't crucial. From reading around .NET doesn't seem to easily support writing bitonal images, so we're ending up with ~1MB files instead of ~100K ones. I'm considering using ImageMagick to do this, but ideally i'd like a solution which doesn't require this if possible.
Current code snippet (which also does some resizing on the image):
using (Image img = Image.FromFile(imageName))
{
using (Bitmap resized = new Bitmap(resizedWidth, resizedHeight)
{
using (Graphics g = Graphics.FromImage(resized))
{
g.DrawImage(img, new Rectangle(0, 0, resized.Width, resized.Height), 0, 0, img.Width, img.Height, GraphicsUnit.Pixel);
}
resized.Save(outputFilename, System.Drawing.Imaging.ImageFormat.Jpeg);
}
}
Is there any way to achieve this?
Thanks.
I believe the problem can be solved by checking that resized bitmap is of PixelFormat.Format1bppIndexed. If it's not, you should convert it to 1bpp bitmap and after that you can save it as black and white png without problems.
In other words, you should use following code instead of resized.Save(outputFilename, System.Drawing.Imaging.ImageFormat.Jpeg);
if (resized.PixelFormat != PixelFormat.Format1bppIndexed)
{
using (Bitmap bmp = convertToBitonal(resized))
bmp.Save(outputFilename, System.Drawing.Imaging.ImageFormat.Png);
}
else
{
resized.Save(outputFilename, System.Drawing.Imaging.ImageFormat.Png);
}
I use following code for convertToBitonal :
private static Bitmap convertToBitonal(Bitmap original)
{
int sourceStride;
byte[] sourceBuffer = extractBytes(original, out sourceStride);
// Create destination bitmap
Bitmap destination = new Bitmap(original.Width, original.Height,
PixelFormat.Format1bppIndexed);
destination.SetResolution(original.HorizontalResolution, original.VerticalResolution);
// Lock destination bitmap in memory
BitmapData destinationData = destination.LockBits(
new Rectangle(0, 0, destination.Width, destination.Height),
ImageLockMode.WriteOnly, PixelFormat.Format1bppIndexed);
// Create buffer for destination bitmap bits
int imageSize = destinationData.Stride * destinationData.Height;
byte[] destinationBuffer = new byte[imageSize];
int sourceIndex = 0;
int destinationIndex = 0;
int pixelTotal = 0;
byte destinationValue = 0;
int pixelValue = 128;
int height = destination.Height;
int width = destination.Width;
int threshold = 500;
for (int y = 0; y < height; y++)
{
sourceIndex = y * sourceStride;
destinationIndex = y * destinationData.Stride;
destinationValue = 0;
pixelValue = 128;
for (int x = 0; x < width; x++)
{
// Compute pixel brightness (i.e. total of Red, Green, and Blue values)
pixelTotal = sourceBuffer[sourceIndex + 1] + sourceBuffer[sourceIndex + 2] +
sourceBuffer[sourceIndex + 3];
if (pixelTotal > threshold)
destinationValue += (byte)pixelValue;
if (pixelValue == 1)
{
destinationBuffer[destinationIndex] = destinationValue;
destinationIndex++;
destinationValue = 0;
pixelValue = 128;
}
else
{
pixelValue >>= 1;
}
sourceIndex += 4;
}
if (pixelValue != 128)
destinationBuffer[destinationIndex] = destinationValue;
}
Marshal.Copy(destinationBuffer, 0, destinationData.Scan0, imageSize);
destination.UnlockBits(destinationData);
return destination;
}
private static byte[] extractBytes(Bitmap original, out int stride)
{
Bitmap source = null;
try
{
// If original bitmap is not already in 32 BPP, ARGB format, then convert
if (original.PixelFormat != PixelFormat.Format32bppArgb)
{
source = new Bitmap(original.Width, original.Height, PixelFormat.Format32bppArgb);
source.SetResolution(original.HorizontalResolution, original.VerticalResolution);
using (Graphics g = Graphics.FromImage(source))
{
g.DrawImageUnscaled(original, 0, 0);
}
}
else
{
source = original;
}
// Lock source bitmap in memory
BitmapData sourceData = source.LockBits(
new Rectangle(0, 0, source.Width, source.Height),
ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
// Copy image data to binary array
int imageSize = sourceData.Stride * sourceData.Height;
byte[] sourceBuffer = new byte[imageSize];
Marshal.Copy(sourceData.Scan0, sourceBuffer, 0, imageSize);
// Unlock source bitmap
source.UnlockBits(sourceData);
stride = sourceData.Stride;
return sourceBuffer;
}
finally
{
if (source != original)
source.Dispose();
}
}
Have you tried saving using the Image.Save overload with Encoder parameters?
Like the Encoder.ColorDepth Parameter?
Trying jaroslav's suggestion for color depth doesn't work:
static void Main(string[] args)
{
var list = ImageCodecInfo.GetImageDecoders();
var jpegEncoder = list[1]; // i know this is the jpeg encoder by inspection
Bitmap bitmap = new Bitmap(500, 500);
Graphics g = Graphics.FromImage(bitmap);
g.DrawRectangle(new Pen(Color.Red), 10, 10, 300, 300);
var encoderParams = new EncoderParameters();
encoderParams.Param[0] = new EncoderParameter(Encoder.ColorDepth, 2);
bitmap.Save(#"c:\newbitmap.jpeg", jpegEncoder, encoderParams);
}
The jpeg is still a full color jpeg.
I don't think there is any support for grayscale jpeg in gdi plus. Have you tried looking in windows imaging component?
http://www.microsoft.com/downloads/details.aspx?FamilyID=8e011506-6307-445b-b950-215def45ddd8&displaylang=en
code example: http://www.codeproject.com/KB/GDI-plus/windows_imaging.aspx
wikipedia: http://en.wikipedia.org/wiki/Windows_Imaging_Component
This is an old thread. However, I'll add my 2 cents.
I use AForge.Net libraries (open source)
use these dlls. Aforge.dll, AForge.Imaging.dll
using AForge.Imaging.Filters;
private void ConvertBitmap()
{
markedBitmap = Grayscale.CommonAlgorithms.RMY.Apply(markedBitmap);
ApplyFilter(new FloydSteinbergDithering());
}
private void ApplyFilter(IFilter filter)
{
// apply filter
convertedBitmap = filter.Apply(markedBitmap);
}
Have you tried PNG with 1 bit color depth?
To achieve a size similar to a CCITT4 TIFF, I believe your image needs to use a 1-bit indexed pallette.
However, you can't use the Graphics object in .NET to draw on an indexed image.
You will probably have to use LockBits to manipulate each pixel.
See Bob Powell's excellent article.