I write a method which reduce the color depth (GIF convertion) and set the resolution to 600pixel from a Bitmap.
The color depth convertion works fine but the resolution set is not working.
<script runat="server" language="C#">
public static void Convert(Bitmap oldbmp, String path)
{
System.Drawing.Bitmap bm8Bit;
using (MemoryStream ms = new MemoryStream())
{
oldbmp.Save(ms, ImageFormat.Gif);
ms.Position = 0;
bm8Bit = (System.Drawing.Bitmap) System.Drawing.Image.FromStream(ms);
bm8Bit.SetResolution(600, 600);
bm8Bit.Save(path, System.Drawing.Imaging.ImageFormat.Bmp);
return;
}
}
</script>
I think you can set resolution only on new Bitmap, that wan't saved before, so if you have exiting bitmap, you need to copy it to new Bitmap instance:
Bitmap imgCopy = new Bitmap(img);
imgCopy.SetResolution(600.0f,600.0f);
Read here: http://msdn.microsoft.com/en-us/library/system.drawing.bitmap.setresolution.aspx
Use this method to set the desired resolution on a newly created bitmap. Changing the resolution of the image does not change its physical size.
Hey its always good to try to write our own solution but their is a library available which is easy to implement and works really great
you might want to use it, you can download it from here and it have very good documentation too.
http://imageresizing.net/download
Related
How would you copy a part from one WriteableBitmap to another WriteableBitmap? I've written and used dozens of 'copypixel' and transparent copies in the past, but I can't seem to find the equivalent for WPF C#.
This is either the most difficult question in the world or the easiest because absolutely nobody is touching it with a ten foot pole.
Use WriteableBitmapEx from http://writeablebitmapex.codeplex.com/
Then use the Blit method as below.
private WriteableBitmap bSave;
private WriteableBitmap bBase;
private void test()
{
bSave = BitmapFactory.New(200, 200); //your destination
bBase = BitmapFactory.New(200, 200); //your source
//here paint something on either bitmap.
Rect rec = new Rect(0, 0, 199, 199);
using (bSave.GetBitmapContext())
{
using (bBase.GetBitmapContext())
{
bSave.Blit(rec, bBase, rec, WriteableBitmapExtensions.BlendMode.Additive);
}
}
}
you can use BlendMode.None for higher performance if you don't need to preserve any information in your destination. When using Additive you get alpha compositing between source and destination.
There does not appear to be a way to copy directly from one to another but you can do it in two steps using an array and CopyPixels to get them out of one and then WritePixels to get them into another.
I agree with Guy above that the easiest method is to simply use the WriteableBitmapEx library; however, the Blit function is for compositing a foreground and background image. The most efficient method to copy a part of one WriteableBitmap to another WriteableBitmap would be to use the Crop function:
var DstImg = SrcImg.Crop(new Rect(...));
Note that your SrcImg WriteableBitmap must be in the Pbgra32 format to be operated on by the WriteableBitmapEx library. If your bitmap isn't in this form, then you can easily convert it before cropping:
var tmp = BitmapFactory.ConvertToPbgra32Format(SrcImg);
var DstImg = tmp.Crop(new Rect(...));
public static void CopyPixelsTo(this BitmapSource sourceImage, Int32Rect sourceRoi, WriteableBitmap destinationImage, Int32Rect destinationRoi)
{
var croppedBitmap = new CroppedBitmap(sourceImage, sourceRoi);
int stride = croppedBitmap.PixelWidth * (croppedBitmap.Format.BitsPerPixel / 8);
var data = new byte[stride * croppedBitmap.PixelHeight];
// Is it possible to Copy directly from the sourceImage into the destinationImage?
croppedBitmap.CopyPixels(data, stride, 0);
destinationImage.WritePixels(destinationRoi,data,stride,0);
}
I am writing C# lib for very simple recognize image to use it in monodroid and also using zxing port to C#. But after I read image bytes from file I do such thing, same as in zxing barcode scanning.
binaryBitmap = new BinaryBitmap(new HybridBinarizer(new RGBLuminanceSource(rawRgb, width, height, format)));
But somehow it reverse image by vertical. I just saving binaryBitmap as bitmap to file by pixels.
Please help me understand why it's happen? What am I doing wrong?
#Michael am using Zxing.Net.Mobile port, from here https://github.com/Redth/ZXing.Net.Mobile. It's very weird for me it I am using PlanarYUVLuminanceSource - then I get such image http://i.imgur.com/OlwqC0I.png, but if I am using RGBLuminanceSource then I get full almost normal image, see example image. so now I have even 2 questions:
why planar take only part of image and have "layer on layer" effect? and
ok if I will use RGBLuminanceSource then, why it have some invertion of colors, I mean somewhere rectangles border is black and somewhere they are white. because it real image they all black?
UPDATE:
Here is how I get bytes from device and also as you see I set nv21 format, so it must be YUV, no? I wonder, what I am doing wrong that rgb source work(at list image is ok) and PLanarYUV not :((
BTW, original byte from preview frame have result and same file size.
Any suggestion?
public void OnPreviewFrame(byte[] bytes, Android.Hardware.Camera camera)
{
var img = new YuvImage(bytes, ImageFormatType.Nv21, cameraParameters.PreviewSize.Width, cameraParameters.PreviewSize.Height, null); string _fileName2 = "YUV_BYtes_"+ DateTime.Now.Ticks +".txt";
string pathToFile2 = Path.Combine(Android.OS.Environment.ExternalStorageDirectory.AbsolutePath, _fileName2);
using (var fileStream = new FileStream(pathToFile2, FileMode.Append, FileAccess.Write, FileShare.None))
{
fileStream.Write(img.GetYuvData(), 0, img.GetYuvData().Length);
}
}
public void SurfaceChanged(ISurfaceHolder holder, global::Android.Graphics.Format format, int width, int height)
{
if (camera == null)
return;
var parameters = camera.GetParameters();
width = parameters.PreviewSize.Width;
height = parameters.PreviewSize.Height;
parameters.PreviewFormat = ImageFormatType.Nv21;
//parameters.PreviewFrameRate = 15;
//this.height = size.height;
//this.width = size.width;
//camera.setParameters( params );
//parameters.PreviewFormat = ImageFormatType.;
camera.SetParameters(parameters);
camera.SetDisplayOrientation(90);
camera.StartPreview();
cameraResolution = new Size(parameters.PreviewSize.Width, parameters.PreviewSize.Height);
AutoFocus();
}
I think I know what you have done. The data looks like RGB565 bitmap data (or something similar). You can't put such a byte array into the PlanarYUVLuminanceSource. You have to make sure that the byte array which you use with the planar source is really a array with only yuv data, not RGB565.
The rules are easy:
if you use the following code snippet
new RGBLuminanceSource(rawRgb, width, height, format)
make sure that the value of format matches the layout and data of the parameter rawRgb.
if you use somethin glike the following
new PlanarYUVLuminanceSource(yuvBytes, 640, 960, 0, 0, 640, 960, false);
make sure that yuvBytes only contains real yuv data.
I can only give a better answer if you post a more complete code sample.
Is there a way to resize an image using GPU (graphic card) that is consumable through a .NET application?
I am looking for an extremely performant way to resize images and have heard that the GPU could do it much quicker than CPU (GDI+ using C#). Are there known implementations or sample code using the GPU to resize images that I could consume in .NET?
Have you thought about using XNA to resize your images? Here you can find out how to use XNA to save image as a png/jpeg to a MemoryStream and later reuse it a Bitmap object:
EDIT: I will post an example here (taken from the link above) on how you can possibly use XNA.
public static Image Texture2Image(Texture2D texture)
{
Image img;
using (MemoryStream MS = new MemoryStream())
{
texture.SaveAsPng(MS, texture.Width, texture.Height);
//Go To the beginning of the stream.
MS.Seek(0, SeekOrigin.Begin);
//Create the image based on the stream.
img = Bitmap.FromStream(MS);
}
return img;
}
I also found out today that you can OpenCV to use GPU/multicore CPUs. You can for example choose to use a .NET wrapper such as Emgu and and use its Image class to manipulate with your picture and return a .NET Bitmap class:
public static Bitmap ResizeBitmap(Bitmap sourceBM, int width, int height)
{
// Initialize Emgu Image object
Image<Bgr, Byte> img = new Image<Bgr, Byte>(sourceBM);
// Resize using liniear interpolation
img.Resize(width, height, INTER.CV_INTER_LINEAR);
// Return .NET Bitmap object
return img.ToBitmap();
}
I wrote a quick spike to check performance using WPF, though I cannot for sure say that its using the GPU.
Still, see below. This scales an image to 33.5 (or whatever) times its original size.
public void Resize()
{
double scaleFactor = 33.5;
var originalFileStream = System.IO.File.OpenRead(#"D:\SkyDrive\Pictures\Random\Misc\DoIt.jpg");
var originalBitmapDecoder = JpegBitmapDecoder.Create(originalFileStream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
BitmapFrame originalBitmapFrame = originalBitmapDecoder.Frames.First();
var originalPixelFormat = originalBitmapFrame.Format;
TransformedBitmap transformedBitmap =
new TransformedBitmap(originalBitmapFrame, new System.Windows.Media.ScaleTransform()
{
ScaleX = scaleFactor,
ScaleY = scaleFactor
});
int stride = ((transformedBitmap.PixelWidth * transformedBitmap.Format.BitsPerPixel) + 7) / 8;
int pixelCount = (stride * (transformedBitmap.PixelHeight - 1)) + stride;
byte[] buffer = new byte[pixelCount];
transformedBitmap.CopyPixels(buffer, stride, 0);
WriteableBitmap transformedWriteableBitmap = new WriteableBitmap(transformedBitmap.PixelWidth, transformedBitmap.PixelHeight, transformedBitmap.DpiX, transformedBitmap.DpiY, transformedBitmap.Format, transformedBitmap.Palette);
transformedWriteableBitmap.WritePixels(new Int32Rect(0, 0, transformedBitmap.PixelWidth, transformedBitmap.PixelHeight), buffer, stride, 0);
BitmapFrame transformedFrame = BitmapFrame.Create(transformedWriteableBitmap);
var jpegEncoder = new JpegBitmapEncoder();
jpegEncoder.Frames.Add(transformedFrame);
using (var outputFileStream = System.IO.File.OpenWrite(#"C:\DATA\Scrap\WPF.jpg"))
{
jpegEncoder.Save(outputFileStream);
}
}
The image I was testing was 495 x 360. It resized it to over 16k x 12k in a couple of seconds, including save out.
It resizes to 1.5x around 165 times a second in a single-core run. On an i7 and the GPU seemingly doing nothing, CPU at 20% I'd expect to get 5x more when multithreaded.
Performance profiling shows a hot path to wpfgfx_v0400.dll which is the native WPF graphics library and is close to DirectX (look-up 'milcore' in Google).
So it might be accelerated, I don't know.
Luke
Yes, it is possible to use GPU to resize your images. This can be done using DirectX Surfaces (for example using SlimDx in C#). You should create a surface and move your image to it, and then you can stretch this surface to another target surface of your desired size using only GPU, and finally get back the resized image from the target surface. In these scenario, pixel format of the surfaces can be different and the GPU automatically handles it. But here there are things that can affect the performance of this operation. Moving data between GPU and CPU is a time consuming process. You can apply some techniques to boost performance based on your situation, and avoiding extra data transfer between CPU and GPU memory.
My scenario is the following. I am creating a little math quiz application for my son and wanted to dynamically change the background ImageBrush of my canvas after each question is answered. I proceeded to embed my images (pngs) into a resource file and figured I would load them into an array and then randomly choose one and load it into the canvas.
The first problem I ran into was of course the fact that in the resource file the images were being stored as Bitmaps. So after some looking around on the Internet I finally figured out how to get them converted from Bitmap to BitmapImage objects using the following helper method:
private BitmapImage FromResourceBitmap(Bitmap bitmap)
{
var result = new BitmapImage();
using(var stream = new MemoryStream())
{
bitmap.Save(stream, ImageFormat.Png);
stream.Position = 0;
result.BeginInit();
result.StreamSource = stream;
result.EndInit();
}
return result;
}
From there I created an ImageBrush from the BitmapImage and assigned it to the Background property of the canvas:
var brush = new ImageBrush {ImageSource = m_Media.Screens[0]}; // m_Media.Screens[x] returns a BitmapImage...obviously.
QuestionCanvas.Background = brush;
Unfortunately, this doesn't seem to work. When the application runs the background is pure white. My XAML doesn't describe any backgrounds and...well I'm confused. Any help would be greatly appreciated! Thank you!
I'm wondering if perhaps your canvas is transparent, or perhaps you have another element on top of the canvas. I would take a look at Snoop on Codeplex to look at your visual tree and identify exactly what is going on. Also consider using triggers or codebinding to set the image for you when you move to the next question. Then you could just bind the Background to the template with the trigger, or the item holding the image and have it automatically update.
After looking through the various ImageBrush related classes, especially that of BitmapImage I started to think that the StreamSource property was simply referencing the stream instead of making a local copy within the BitmapImage object. So, the using statement within my helper method was in effect releasing the stream and therefore making the StreamSource of the BitmapImage null. The canvas then fell back to a plain white (#FFFFFFFF) SolidColorBrush (Thanks James for reminding me of Snoop).
So, to fix this I instead created a private List to hold the references to the various image streams and then point my BitmapImages to those references. I implemented IDisposable to release the various MemoryStreams when the GC came along. Here is the abbreviated code:
public class Media : IDisposable
{
private readonly List<BitmapImage> m_Screens = new List<BitmapImage>();
private readonly List<MemoryStream> m_BackingStreams = new List<MemoryStream>();
public BitmapImage MainScreen { get; private set; }
public List<BitmapImage> Screens
{
get
{
return m_Screens;
}
}
public Media()
{
LoadScreens();
}
private void LoadScreens()
{
m_BackingStreams.Add(FromResourceBitmap(Properties.Resources.Screen_01));
m_BackingStreams.Add(FromResourceBitmap(Properties.Resources.Screen_02));
m_BackingStreams.Add(FromResourceBitmap(Properties.Resources.Screen_03));
m_BackingStreams.Add(FromResourceBitmap(Properties.Resources.Screen_04));
m_BackingStreams.Add(FromResourceBitmap(Properties.Resources.Screen_05));
foreach (var stream in m_BackingStreams)
{
m_Screens.Add(FromResourceStream(stream));
}
}
private BitmapImage FromResourceStream(Stream stream)
{
var result = new BitmapImage();
result.BeginInit();
result.StreamSource = stream;
result.EndInit();
return result;
}
private MemoryStream FromResourceBitmap(Bitmap bitmap)
{
var stream = new MemoryStream();
bitmap.Save(stream, ImageFormat.Png);
return stream;
}
public void Dispose()
{
if (m_BackingStreams.Count == 0 || m_BackingStreams == null) return;
foreach (var stream in m_BackingStreams)
{
stream.Close();
stream.Flush();
}
}
And here is what it looked like when I actually set the background of my Canvas during runtime:
MainMenuCanvas.Background = new ImageBrush(m_Media.Screens[0]);
That fixed it, as inelegant as it may be.
While researching I did come across this little bit of info in the BitmapImage.StreamSource documentation page on MSDN:
Set the CacheOption property to
BitmapCacheOption.OnLoad if you wish
to close the stream after the
BitmapImage is created. The default
OnDemand cache option retains access
to the stream until the bitmap is
needed, and cleanup is handled by the
garbage collector.
( http://bit.ly/bitmapimagestreamsource )
However, when I tried to use the original solution with the CacheOption enum set to BitmapCacheOption.OnLoad it resulted in the same problem. I may be missing something here, but the obvious isn't so obvious I guess. :)
I have a fair few images that I'm loading into a ListBox in my WPF application. Originally I was using GDI to resize the images (the originals take up far too much memory). That was fine, except they were taking about 400ms per image. Not so fine. So in search of another solution I found a method that uses TransformedBitmap (which inherits from BitmapSource). That's great, I thought, I can use that. Except I'm now getting memory leaks somewhere...
I'm loading the images asynchronously using a BackgroundWorker like so:
BitmapSource bs = ImageUtils.ResizeBitmapSource(ImageUtils.GetImageSource(photo.FullName));
//BitmapSource bs = ImageUtils.GetImageSource(photo.FullName);
bs.Freeze();
this.dispatcher.Invoke(new Action(() => { photo.Source = bs; }));
GetImageSource just gets the Bitmap from the path and then converts to BitmapSource.
Here's the code snippet for ResizeBitmapSource:
const int thumbnailSize = 200;
int width;
int height;
if (bs.Width > bs.Height)
{
width = thumbnailSize;
height = (int)(bs.Height * thumbnailSize / bs.Width);
}
else
{
height = thumbnailSize;
width = (int)(bs.Width * thumbnailSize / bs.Height);
}
BitmapSource tbBitmap = new TransformedBitmap(bs,
new ScaleTransform(width / bs.Width,
height / bs.Height, 0, 0));
return tbBitmap;
That code is essentially the code from:
http://rongchaua.net/blog/c-wpf-fast-image-resize/
Any ideas what could be causing the leak?
edit:
Here's the code for GetImageSource, as requested
using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read))
{
using (var bmp = Image.FromStream(stream, false, false))
{
// Use WPF to resize
var bitmapSource = ConvertBitmapToBitmapSource(bmp);
bitmapSource = ResizeBitmapSource(bitmapSource);
return bitmapSource;
}
}
I think you misunderstood how the TransformedBitmap works. It holds onto a reference to the source bitmap, and transforms it in memory. Maybe you could encode the transformed bitmap into a memory stream, and read it right back out. I'm not sure how fast this would be, but you wouldn't then be holding on to the full sized bitmap.
I found this blog post that returned a WriteableBitmap with the TransformedBitmap as the source. The WriteableBitmap will copy the pixel data to a memory buffer in the initializer, so it doesn't actually hold on to a reference to the TransformedBitmap, or the full sized image.
At a guess, from looking at your code you might need to dispose of the bitmap returned by the call to ImageUtils.GetImageSource(photo.FullName).
I have also noted on the blog you pointed out that the author has added an update (11th of March) about inserting a using statement to prevent memory leaks.