I have document processor that extract thumbnails and Large images from tiff file.
But i get "file already in use" and "A generic error occurred in GDI+" error most frequently.
There three basic functions:
Get tiff pages count
Get all thumbnails
Get all large images
The file stream is used in all three operations.
Is there any way that I can use the file stream once, save a copy of the file in memory and release the file for other use.
I tried this:
using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.ReadWrite))
{
photo = Image.FromStream(fs, true, false);
fs.Flush();
fs.Close();
}
using (photo)
{
//dummy hight and width passed to function.
generateThumbNails(photo,1,90,90);
System.Drawing.Imaging.FrameDimension dimention = new FrameDimension(photo.FrameDimensionsList[0]);
total = photo.GetFrameCount(dimention);
photo.Dispose();
}
generateThumbNails function:
public static Bitmap GenerateThumbNails(System.Drawing.Image img, int curPageNo, int width, int height)
{
System.Drawing.Imaging.FrameDimension dimention = new FrameDimension(img.FrameDimensionsList[0]);
img.SelectActiveFrame(dimention, curPageNo);
Bitmap bmp = new Bitmap(width, height);
Graphics g = Graphics.FromImage(bmp);
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
g.DrawImage(img, 0, 0, width, height);
g.Dispose();
return bmp;
}
But I get "A generic error occurred in GDI+" while re-sizing the image on the following line:
g.DrawImage(img, 0, 0, width, height);
Related
I have an ASP.NET Core 3.1 WebAPI that is responsible for providing other apps I have with images. I am trying to improve the performance of my apps. One of the feedback I get from running PageSpeed Insights test is to Serve images in next-gen formats.
All of the images that my WebAPI server are in JPEG format. I don't know much about image compression or image-formats, but I would like to evaluate the results from converting JPEG images to WebP as study show that WebP leads to better results.
I use the following ImageProcessor class to save uploaded images to my WebAPI.
public class ImageProcessor
{
public Image GetResizedImage(Stream stream, int newWidth, out ImageFormat imageFormat)
{
Image sourceImage = Image.FromStream(stream);
SizeF thumbSize = GetNewSize(sourceImage, newWidth);
imageFormat = sourceImage.RawFormat;
return ResizeImage(sourceImage, (int)thumbSize.Width, (int)thumbSize.Height);
}
protected SizeF GetNewSize(Image img, int newMaxWidth)
{
var size = new SizeF
{
Width = newMaxWidth,
Height = (newMaxWidth * img.Height) / img.Width
};
return size;
}
protected Bitmap ResizeImage(Image image, int width, int height)
{
var destRect = new Rectangle(0, 0, width, height);
var destImage = new Bitmap(width, height);
var xRes = Math.Max(Math.Min(image.HorizontalResolution, 96), 72);
var yRes = Math.Max(Math.Min(image.VerticalResolution, 96), 72);
destImage.SetResolution(xRes, yRes);
using (Graphics graphics = Graphics.FromImage(destImage))
{
graphics.CompositingMode = CompositingMode.SourceCopy;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
using (ImageAttributes wrapMode = new ImageAttributes())
{
wrapMode.SetWrapMode(WrapMode.TileFlipXY);
graphics.DrawImage(image, destRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, wrapMode);
}
}
return destImage;
}
}
Here is how I call the ImageProcessor class to store images on disk from the controller
public async Task<IActionResult> Store(IFormFile file)
{
if(!ModelState.IsValid)
{
return Problem("Invalid model!");
}
// Process Image
Image image = GetResizedImage(file.OpenReadStream(), 1024, out ImageFormat imageFormat);
MemoryStream memoryStream = new MemoryStream();
image.Save(memoryStream, imageFormat);
memoryStream.Seek(0, SeekOrigin.Begin);
// Store it on disk
string fullPath = "full path to the new image";
Directory.CreateDirectory(Path.GetDirectoryName(fullPath));
using FileStream cacheWriter = new FileStream(fullPath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Write, 4096, true);
await memoryStream.CopyToAsync(cacheWriter);
return Ok();
}
Question
How can I store the image in a WebP format using .Net Core?
If I were you I would look at installing an instance of Thumbor or using Cloudinary to generate your webp files.
Then you can use JS to conditionally load the correct file.
EDIT: Add method where error actually occurs...
I am opening an image and I want to be able to overwrite the original file as modification happens. I tryed both of the methods here
public ImgPro(String Path)
{
Bitmap bt1 = new Bitmap(Path);
Bitmap bt2 = new Bitmap(bt1.Width, bt1.Height, PixelFormat.Format24bppRgb);
var imgRec = new Rectangle(0, 0, bt1.Width, bt1.Height);
Graphics bt2G = Graphics.FromImage(bt2);
bt2G.DrawImage(bt1, imgRec);
bt1.Dispose();
this.bitmap = bt2;
}
And
public ImgPro(String Path)
{
Bitmap bt1 = new Bitmap(Path);
Bitmap bt2 = new Bitmap(bt1.Width, bt1.Height, PixelFormat.Format24bppRgb);
var imgRec = new Rectangle(0, 0, bt1.Width, bt1.Height);
BitmapData bt1Data = bt1.LockBits(imgRec, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
BitmapData bt2Data = bt2.LockBits(imgRec, ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);
// Create data array to hold bmpSource pixel data
int numBytes = bt1Data.Stride * (int)bt1.Height;
var srcData = new byte[numBytes];
var destData = new byte[numBytes];
Marshal.Copy(bt1Data.Scan0, srcData, 0, numBytes);
Array.Copy(srcData, destData, srcData.Length);
Marshal.Copy(destData, 0, bt2Data.Scan0, numBytes);
bt1.UnlockBits(bt1Data); bt2.UnlockBits(bt2Data);
bt1.Dispose();
this.bitmap = bt2;
}
But both options failed when I went to save the file I got this error.
An unhandled exception of type 'System.Runtime.InteropServices.ExternalException' occurred in System.Drawing.dll
For this method:
public void Save(string filename)
{
bitmap.Save(filename, ImageFormat.Jpeg);
}
Since Bitmap locks the underlying stream, you could copy the file contents to a MemoryStream and base the Bitmap on that instead. This should prevent the file from being locked:
var bytes = File.ReadAllBytes(Path);
using (var stream = new MemoryStream(bytes)) // Don't dispose this until you're done with your Bitmap
{
Bitmap bt1 = new Bitmap(stream);
// ...
}
I'm trying to stream Kinect video data (just the image, not depth/infared) but I find the default buffer size on the image is very large (1228800) and incapable of sending over a network. I was wondering if there was any way of getting access to a smaller array without having to go down the route of codec compression. Here's is how I declare the Kinect which I took from a Microsoft sample;
// Turn on the color stream to receive color frames
this.sensor.ColorStream.Enable(ColorImageFormat.RgbResolution640x480Fps30);
// Allocate space to put the pixels we'll receive
this.colorPixels = new byte[this.sensor.ColorStream.FramePixelDataLength];
// This is the bitmap we'll display on-screen
this.colorBitmap = new WriteableBitmap(this.sensor.ColorStream.FrameWidth,
this.sensor.ColorStream.FrameHeight, 96.0, 96.0, PixelFormats.Bgr32, null);
// Set the image we display to point to the bitmap where we'll put the image data
this.kinectVideo.Source = this.colorBitmap;
// Add an event handler to be called whenever there is new color frame data
this.sensor.ColorFrameReady += this.SensorColorFrameReady;
// Start the sensor!
this.sensor.Start();
And here is the New Frame event which I then try to send each frame;
private void SensorColorFrameReady(object sender,
ColorImageFrameReadyEventArgs e)
{
using (ColorImageFrame colorFrame = e.OpenColorImageFrame())
{
if (colorFrame != null)
{
// Copy the pixel data from the image to a temporary array
colorFrame.CopyPixelDataTo(this.colorPixels);
// Write the pixel data into our bitmap
this.colorBitmap.WritePixels(
new Int32Rect(0, 0, this.colorBitmap.PixelWidth,
this.colorBitmap.PixelHeight),
this.colorPixels,
this.colorBitmap.PixelWidth * sizeof(int),
0);
if (NetworkStreamEnabled)
{
networkStream.Write(this.colorPixels, 0,
this.colorPixels.GetLength(0));
}
}
}
}
UPDATE
I'm using the following two methods to convert the ImageFrame to a Bitmap and then the Bitmap to a Byte[]. This has brought the buffer size down to ~730600. Still not enough but progress. (Source: Convert Kinect ColorImageFrame to Bitmap)
public static byte[] ImageToByte(Image img)
{
ImageConverter converter = new ImageConverter();
return (byte[])converter.ConvertTo(img, typeof(byte[]));
}
Bitmap ImageToBitmap(ColorImageFrame Image)
{
byte[] pixeldata = new byte[Image.PixelDataLength];
Image.CopyPixelDataTo(pixeldata);
Bitmap bmap = new Bitmap(Image.Width, Image.Height, System.Drawing.Imaging.PixelFormat.Format32bppRgb);
BitmapData bmapdata = bmap.LockBits(
new Rectangle(0, 0, Image.Width, Image.Height),
ImageLockMode.WriteOnly,
bmap.PixelFormat);
IntPtr ptr = bmapdata.Scan0;
Marshal.Copy(pixeldata, 0, ptr, Image.PixelDataLength);
bmap.UnlockBits(bmapdata);
return bmap;
}
My recommendation would be to store the colorframe in a bitmap, then send those files over the network and reassemble them in a video program. A project I've been doing with the Kinect does this:
//Save to file
if (skeletonFrame != null)
{
RenderTargetBitmap bmp = new RenderTargetBitmap(800, 600, 96, 96, PixelFormats.Pbgra32);
bmp.Render(window.image);
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
// create frame from the writable bitmap and add to encoder
if (skeletonFrame.Timestamp - lastTime > 90)
{
encoder.Frames.Add(BitmapFrame.Create(bmp));
string myPhotos = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures);
string path = "C:your\\directory\\here" + skeletonFrame.Timestamp + ".jpg";
using (FileStream fs = new FileStream(path, FileMode.Create))
{
encoder.Save(fs);
}
lastTime = skeletonFrame.Timestamp;
}
}
Of course, if you need this to be in real time, you're not going to like this solution, and I think my "comment" button is gone after the bounty.
My ASP.NET application has an image cropping and resizing features. This requires that the uploaded temporary image be deleted. Everything works fine, but when I try to delete an image larger than 80px by 80px I get a "File is locked by another process..." error, even though I've released all resources.
Here's a snippet:
System.Drawing.Image tempimg = System.Drawing.Image.FromFile(temppath);
System.Drawing.Image img = (System.Drawing.Image) tempimg.Clone(); //advice from another forum
tempimg.Dispose();
img = resizeImage(img, 200, 200); //delete only works if it's 80, 80
img.Save(newpath);
img.Dispose();
File.Delete(temppath);
I think you are not disposing the first Image instance assigned to the img variable.
Consider this instead:
System.Drawing.Image tempimg = System.Drawing.Image.FromFile(temppath);
System.Drawing.Image img = (System.Drawing.Image) tempimg.Clone();
tempimg.Dispose();
System.Drawing.Image img2 = resizeImage(img, 200, 200);
img2.Save(newpath);
img2.Dispose();
img.Dispose();
File.Delete(temppath);
If you create the image this way, it won't be locked:
using (FileStream fs = new FileStream(info.FullName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
byte[] data = new byte[fs.Length];
int read = fs.Read(data, 0, (int)fs.Length);
MemoryStream ms = new MemoryStream(data, false);
return Image.FromStream(ms, false, false); // prevent GDI from holding image file open
}
I'm doing this paint application. It's kind of simple. It consist of a panel where I will draw on and then finally I will save as JPG or BMP or PNG file.
My application work perfectly but the problem I'm facing is that when I'm saving the output is not what drawn on the panel its black Image nothing just black.
all my work is been saved as
Thepic = new Bitmap(panel1.ClientRectangle.Width, this.ClientRectangle.Height);
and on the mouse (down,up thing) I have
snapshot = (Bitmap)tempDraw.Clone();
and it saved the work normally but again the rsult is black Image not what the panel contain.
I think the problem may be that you're using the "Clone" method.
Try "DrawToBitmap" - that's worked for me in the past.
Here's a sample that saves a bitmap from a control called "plotPrinter":
int width = plotPrinter.Size.Width;
int height = plotPrinter.Size.Height;
Bitmap bm = new Bitmap(width, height);
plotPrinter.DrawToBitmap(bm, new Rectangle(0, 0, width, height));
bm.Save(#"D:\TestDrawToBitmap.bmp", ImageFormat.Bmp);
Be aware of saving directly to the C directly as this is not
permitted with newer versions of window, try using SaveFileDialog.
SaveFileDialog sf = new SaveFileDialog();
sf.Filter = "Bitmap Image (.bmp)|*.bmp|Gif Image (.gif)|*.gif|JPEG Image (.jpeg)|*.jpeg|Png Image (.png)|*.png|Tiff Image (.tiff)|*.tiff|Wmf Image (.wmf)|*.wmf";
sf.ShowDialog();
var path = sf.FileName;
You could try this, this works for me.
I used MemoryStream.
MemoryStream ms = new MemoryStream();
Bitmap bmp = new Bitmap(panel1.Width, panel1.Height);
panel1.DrawToBitmap(bmp, new System.Drawing.Rectangle(0, 0, panel1.Width, panel1.Height));
bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg); //you could ave in BPM, PNG etc format.
byte[] Pic_arr = new byte[ms.Length];
ms.Position = 0;
ms.Read(Pic_arr, 0, Pic_arr.Length);
ms.Close();