i am trying to create thumbnails. my path to original folder is: suppose I:\my images**, and i want to generate it to **i:\new images. i got two problem, first problem is that if my images folder contains subfolder then in the new images it should also be in the sub folder not as a parent folder .
second i got an Error.**A generic error occurred in GDI+.
3rd i get this error: Out of memory.**
its a csharp console application
at System.Drawing.Image.Save(String filename, ImageCodecInfo encoder, EncoderParameters encoderParams)
at System.Drawing.Image.Save(String filename, ImageFormat format)
at ConsoleApplication1.Program.CreateThumbnail(String[] b, Double wid, Double hght, Boolean Isprint)
public void CreateThumbnail(string[] b, double wid, double hght, bool Isprint)
{
string[] path;
path = new string [64];
path = b;
string saveath = "i:\\check\\a test\\";
for (int i = 0; i < b.Length; i++)
{
DirectoryInfo dir = new DirectoryInfo(path[i]);
string dir1 = dir.ToString();
dir1 = dir1.Substring(dir1.LastIndexOf("\\"));
FileInfo[] files1 = dir.GetFiles();
foreach (FileInfo f in files1)
{
string gh = f.ToString();
try
{
System.Drawing.Image myThumbnail150;
System.Drawing.Image.GetThumbnailImageAbort myCallback = new System.Drawing.Image.GetThumbnailImageAbort(ThumbnailCallback);
System.Drawing.Image imagesize = System.Drawing.Image.FromFile(f.FullName);
Bitmap bitmapNew = new Bitmap(imagesize);
double maxWidth = wid;
double maxHeight = hght;
int w = imagesize.Width;
int h = imagesize.Height;
// Longest and shortest dimension
int longestDimension = (w > h) ? w : h;
int shortestDimension = (w < h) ? w : h;
// propotionality
float factor = ((float)longestDimension) / shortestDimension;
// default width is greater than height
double newWidth = maxWidth;
double newHeight = maxWidth / factor;
// if height greater than width recalculate
if (w < h)
{
newWidth = maxHeight / factor;
newHeight = maxHeight;
}
myThumbnail150 = bitmapNew.GetThumbnailImage((int)newWidth, (int)newHeight, myCallback, IntPtr.Zero);
string ext = Path.GetExtension(f.Name);
if (!Directory.Exists(saveath + dir1))
{
Directory.CreateDirectory(saveath + dir1);
myThumbnail150.Save(saveath + dir1 + "\\" + f.Name.Replace(ext, ".Jpeg"), System.Drawing.Imaging.ImageFormat.Jpeg);
}
else if(Directory.Exists(saveath+dir1))
{
myThumbnail150.Save(saveath + dir1+" \\"+ f.Name.Replace(ext, ".Jpeg"), System.Drawing.Imaging.ImageFormat.Jpeg);
}
}
catch (Exception ex)
{
Console.WriteLine("something went wrong" + ex.ToString());
}
}
}
}
I have just refactored a bit the code and now it works (on my machine):
private static void CreateThumbnail(string[] b, double wid, double hght, bool Isprint)
{
string saveAt = "D:\\check";
foreach (string path in b)
{
var directory = new DirectoryInfo(path);
string outputPath = Path.Combine(saveAt, directory.Name);
foreach (FileInfo f in directory.GetFiles("*.*", SearchOption.AllDirectories))
{
if (f.DirectoryName != directory.FullName)
{
outputPath = Path.Combine(saveAt, directory.Name, f.Directory.Name);
}
if (!Directory.Exists(outputPath))
{
Directory.CreateDirectory(outputPath);
}
using (Image imagesize = Image.FromFile(f.FullName))
using (Bitmap bitmapNew = new Bitmap(imagesize))
{
double maxWidth = wid;
double maxHeight = hght;
int w = imagesize.Width;
int h = imagesize.Height;
// Longest and shortest dimension
int longestDimension = (w > h) ? w : h;
int shortestDimension = (w < h) ? w : h;
// propotionality
float factor = ((float)longestDimension) / shortestDimension;
// default width is greater than height
double newWidth = maxWidth;
double newHeight = maxWidth / factor;
// if height greater than width recalculate
if (w < h)
{
newWidth = maxHeight / factor;
newHeight = maxHeight;
}
string fileName = Path.Combine(outputPath, Path.GetFileNameWithoutExtension(f.Name) + ".jpeg");
bitmapNew.GetThumbnailImage((int)newWidth, (int)newHeight, () => false, IntPtr.Zero)
.Save(fileName, System.Drawing.Imaging.ImageFormat.Jpeg);
}
}
}
}
I have to say few things about your old code:
use foreach when possible;
avoid the first three lines, they are useless;
avoid unused variables;
keep your code as clean as possible;
About the "GDI+ generic error" and the "Out Of Memory" exceptions, you should take a look at that link on SO: What is the "best" way to create a thumbnail using ASP.NET?
You must use the C# using statement to make sure unmanaged GDI+ resources underneath the .NET surface are freed as quickly as possible to avoid out of memory errors.
The GDI+ generic can happen if the image cannot be converted for example.
Related
I'm working on a Xamarin app where user's can pick Images using the Xamarin Essentials plugin. My issue now is finding away to shrink the image size using the image full path, before it's loaded to the cloud.
The Code
// Pick Image
private async Task PickImages()
{
if (ImageCollection.Count >= 10)
{
ToastMessageLong("Cannot Select More then 10 Images.");
return;
}
ImageLink image = new();
try
{
FileResult result = await MediaPicker.PickPhotoAsync(new MediaPickerOptions
{
Title = "Pick an Image"
});
if (result == null) return;
image.PostImages = result.FullPath;
ImageCollection.Add(image);
}
catch (Exception x)
{
await DisplayAlert("", x.Message);
}
}
private async Task UploadImagesToCloud()
{
if (ImageCollection.Count > 0)
{
List<ImageLink> imageLinks = new();
foreach (ImageLink img in ImageCollection)
{
// Need to Compress Image before adding to cloud..
ImageLink link = await CloudService.CS.UploadPostImage(img.PostImages);
imageLinks.Add(link);
}
P.Images = imageLinks;
}
}
You could resize the image size befor uploading it to cloud.Here is one method for resizing the image:
#if __IOS__
public static byte[] ResizeImageIOS(byte[] imageData, float width, float height)
{
UIImage originalImage = ImageFromByteArray(imageData);
UIImageOrientation orientation = originalImage.Orientation;
//create a 24bit RGB image
using (CGBitmapContext context = new CGBitmapContext(IntPtr.Zero,
(int)width, (int)height, 8,
4 * (int)width, CGColorSpace.CreateDeviceRGB(),
CGImageAlphaInfo.PremultipliedFirst))
{
RectangleF imageRect = new RectangleF(0, 0, width, height);
// draw the image
context.DrawImage(imageRect, originalImage.CGImage);
UIKit.UIImage resizedImage = UIKit.UIImage.FromImage(context.ToImage(), 0, orientation);
// save the image as a jpeg
return resizedImage.AsJPEG().ToArray();
}
}
#if __ANDROID__
public static byte[] ResizeImageAndroid (byte[] imageData, float width, float height)
{
// Load the bitmap
Bitmap originalImage = BitmapFactory.DecodeByteArray (imageData, 0, imageData.Length);
Bitmap resizedImage = Bitmap.CreateScaledBitmap(originalImage, (int)width, (int)height, false);
using (MemoryStream ms = new MemoryStream())
{
resizedImage.Compress (Bitmap.CompressFormat.Jpeg, 100, ms);
return ms.ToArray ();
}
}
you could refer to ImageResizer
Solution I was Given using SkiaSharp.
public static string CreateThumbnail(string Path, string fileName)
{
var bitmap = SKBitmap.Decode(Path);
int h = bitmap.Height;
int w = bitmap.Width;
int newWidth = w;
int newHeight = h;
//resize algorythm
if (h > 1080 || w > 1080)
{
int rectHeight = 1080;
int rectWidth = 1080;
//aspect ratio calculation
float W = w;
float H = h;
float aspect = W / H;
//new dimensions by aspect ratio
newWidth = (int)(rectWidth * aspect);
newHeight = (int)(newWidth / aspect);
//if one of the two dimensions exceed the box dimensions
if (newWidth > rectWidth || newHeight > rectHeight)
{
//depending on which of the two exceeds the box dimensions set it as the box dimension and calculate the other one based on the aspect ratio
if (newWidth > newHeight)
{
newWidth = rectWidth;
newHeight = (int)(newWidth / aspect);
}
else
{
newHeight = rectHeight;
newWidth = (int)(newHeight * aspect);
}
}
}
var resizedImage = bitmap.Resize(new SKImageInfo(newWidth, newHeight), SKBitmapResizeMethod.Lanczos3);
var image = resizedImage.Encode(SKEncodedImageFormat.Jpeg, 80);
var path = System.Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
var filepath = System.IO.Path.Combine(path, fileName);
string finalPath = filepath;
using (var stream = File.OpenWrite(filepath))
image.SaveTo(stream);
return finalPath;
}
I am extracting info from about 70,000 images and wanted to get their average RGB info. The current code below works, but runs out of memory. So far it just prints out the camera make, model, RGB values and file name. To get started, I found this code, which I adapted:
public static RGBValues AverageRGB(Bitmap bm)
{
BitmapData srcData = bm.LockBits(
new System.Drawing.Rectangle(0, 0, bm.Width, bm.Height),
ImageLockMode.ReadOnly,
System.Drawing.Imaging.PixelFormat.Format32bppArgb);
int stride = srcData.Stride;
IntPtr Scan0 = srcData.Scan0;
long[] totals = new long[] { 0, 0, 0 };
int width = bm.Width;
int height = bm.Height;
unsafe
{
byte* p = (byte*)(void*)Scan0;
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
for (int color = 0; color < 3; color++)
{
int idx = (y * stride) + x * 4 + color;
totals[color] += p[idx];
}
}
}
}
long avgB = totals[0] / (width * height);
long avgG = totals[1] / (width * height);
long avgR = totals[2] / (width * height);
RGBValues rgb = new RGBValues();
rgb.R = avgR;
rgb.G = avgG;
rgb.B = avgB;
return rgb;
}
public class RGBValues
{
public long R;
public long G;
public long B;
}
I then call it like this:
public MainWindow()
{
string folder = #"F:\Photos";
DirSearch(folder);
int counter = 0;
foreach (var filename in filelist)
{
GetImageInfo(filename);
counter++;
if (counter >= 10)
{
GC.Collect();
GC.WaitForPendingFinalizers();
counter = 0;
}
}
InitializeComponent();
}
private void GetImageInfo(string filename)
{
FileInfo info = new FileInfo(filename);
System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
Bitmap bmp = new Bitmap(filename);
PropertyItem[] propItems = bmp.PropertyItems;
var temp = AverageRGB(bmp);
string manufacturer = encoding.GetString(propItems[1].Value);
string model = encoding.GetString(propItems[2].Value);
Console.WriteLine("manufacturer: " + manufacturer + ", model: " + model + ", colorinfo: " + temp.R + ", " + temp.G + ", " + temp.B + ", Filename: " + info.Name);
}
public static void DirSearch(string sDir)
{
try
{
foreach (string d in Directory.GetDirectories(sDir))
{
foreach (string f in Directory.GetFiles(d))
{
filelist.Add(f);
}
DirSearch(d);
}
}
catch (System.Exception excpt)
{
Console.WriteLine(excpt.Message);
}
}
I threw in some garbage collection efforts, but it still runs out of memory after doing around 150 photos. I thought that C# would automatically discard info it didn't need anymore. Is it because this is using unsafe that it continues to hold on to extra info? What can I do to force it to purge old info?
I'm trying to upload an image with an ASP fileupload control but crop it into a square from the middle. this is my code,
public void CropImage(string imagePath)
{
//string imagePath = #"C:\Users\Admin\Desktop\test.jpg";
Bitmap croppedImage;
int x, y;
// Here we capture the resource - image file.
using (var originalImage = new Bitmap(imagePath))
{
if (originalImage.Width > originalImage.Height) {
x = 0;
y = (originalImage.Width / 2) - (originalImage.Height / 2);
}
else if (originalImage.Width < originalImage.Height)
{
y = 0;
x = (originalImage.Height / 2) - (originalImage.Width / 2);
}
else {
y=x=0;
}
int sqrWidth = (originalImage.Width >= originalImage.Height) ? originalImage.Height : originalImage.Width;
Rectangle crop = new Rectangle(x, y, sqrWidth, sqrWidth);
// Here we capture another resource.
croppedImage = originalImage.Clone(crop, originalImage.PixelFormat);
} // Here we release the original resource - bitmap in memory and file on disk.
// At this point the file on disk already free - you can record to the same path.
croppedImage.Save(imagePath, ImageFormat.Jpeg);
// It is desirable release this resource too.
croppedImage.Dispose();
}
//Updates an agent in the database.
protected void BtnUpdate_Click(object sender, EventArgs e)
{
AgentController agentController = new AgentController();
AgentContactController agentContactController = new AgentContactController();
CityController cityController = new CityController();
DistrictController districtController = new DistrictController();
ProvinceController provinceController = new ProvinceController();
CountryController countryController = new CountryController();
InsurancePortalContext context = new InsurancePortalContext();
string folderPath = Server.MapPath("~/AdvisersImages/");
//Check whether Directory (Folder) exists.
if (!Directory.Exists(folderPath))
{
//If Directory (Folder) does not exists. Create it.
Directory.CreateDirectory(folderPath);
}
//Save the File to the Directory (Folder).
string FilePath = folderPath + Path.GetFileName(ImageUploadUpdate.PostedFile.FileName);
if (!File.Exists(FilePath))
{
ImageUploadUpdate.SaveAs(FilePath);
CropImage(FilePath);
}
but I'm getting the out of memory exception at,
croppedImage = originalImage.Clone(crop, originalImage.PixelFormat);
If I don't use the x,y value setting conditional block in CropImage function and/or only save the uploaded image as is, this exception doesn't come.
thanks in advance
As I mentioned in the comments. .Clone() throw an OutOfMemoryException if your crop rect is out of the image's bounds. You can read it up right here:
https://msdn.microsoft.com/de-de/library/ms141944(v=vs.110).aspx
Consider your code with the input image that has the following size:
width: 5px
height: 10px
This is your code.
if(originalImage.Width > originalImage.Height)
{
x = 0;
y = (originalImage.Width / 2) - (originalImage.Height / 2);
}
else if(originalImage.Width < originalImage.Height)
{
y = 0;
x = (originalImage.Height / 2) - (originalImage.Width / 2);
}
else
{
y = x = 0;
}
int sqrWidth = (originalImage.Width >= originalImage.Height) ? originalImage.Height : originalImage.Width;
Rectangle crop = new Rectangle(x, y, sqrWidth, sqrWidth);
The 5x10 image would fall into the else if case.
There y gets set to 0 and x gets set to ((10 / 2) = 5) - (5 / 2) = 2) = 3.
Then sqrWidth gets set to 10.
Next you try to clone the following rect:
x = 3, y = 0, w = 10, h = 10
And voila your rectangle is out of the image's bounds. You need to fix your cropping logic.
The image gets distorted while cropping it into various sizes. How do I do this without affecting the image quality? My current result is a distorted blurry image. Please help.
Here is my code:
var common = new Common();
string filesPath = HttpContext.Current.Server.MapPath(Const.directoryPath);
string imageUrl1 = UploadImageToAzure(1123, "\\Configurator\\_trunk\\Content\\TempImages\\eddaec5aa33e4b1593b304674a842874.jpeg, "eddaec5aa33e4b1593b304674a842874_260x190.jpeg", cloudstorage, containerName);
string cropimage260x190 = CropImagewithName(inputStream/*System.Io.Stream*/, 260, 190, cropedImageName);
public string CropImagewithName(Stream stream, int width, int height, string name)
{
int bmpW = 0;
int bmpH = 0;
string filePath = string.Empty;
string imgName = string.Empty;
try
{
{
bmpW = width;
bmpH = height;
int newWidth = bmpW;
int newHeight = bmpH;
imgName = name;
imgName = imgName.Replace("-", "");
filePath = Const.directoryPath + imgName;
this.upBmp = new Bitmap(stream);
this.newBmp = new Bitmap(newWidth, newHeight, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
this.newBmp.SetResolution(300, 300);
int upWidth = this.upBmp.Width;
int upHeight = this.upBmp.Height;
int newX = 0;
int newY = 0;
decimal reDuce;
if (upWidth > upHeight)
{
reDuce = Convert.ToDecimal(newWidth) / Convert.ToDecimal(upWidth);
newHeight = Convert.ToInt32((Convert.ToDecimal(upHeight) * reDuce));
newY = (bmpH - newHeight) / 2;
newX = 0;
}
else if (upWidth < upHeight)
{
reDuce = Convert.ToDecimal(newHeight) / Convert.ToDecimal(upHeight);
newWidth = Convert.ToInt32((Convert.ToDecimal(upWidth) * reDuce));
newX = (bmpW - newWidth) / 2;
newY = 0;
}
else if (upWidth == upHeight) //
{
reDuce = Convert.ToDecimal(newHeight) / Convert.ToDecimal(upHeight);
newWidth = Convert.ToInt32((Convert.ToDecimal(upWidth) * reDuce));
newX = (bmpW - newWidth) / 2;
newY = (bmpH - newHeight) / 2;
}
newGraphic = Graphics.FromImage(newBmp);
this.newGraphic.Clear(Color.White);
this.newGraphic.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
this.newGraphic.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
newGraphic.DrawImage(upBmp, newX, newY, newWidth, newHeight);
newBmp.Save(HttpContext.Current.Server.MapPath(filePath), System.Drawing.Imaging.ImageFormat.Jpeg);
}
}
catch (Exception)
{
}
finally
{
this.upBmp.Dispose();
this.newBmp.Dispose();
this.newGraphic.Dispose();
}
return imgName;
}
You are experiencing JPEG compression artifacts, not geometrical distortion. You need to set JPEG compression quality before saving your image. Here is how you can save your image with highest quality (look for 100L in the code below):
ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders();
foreach (ImageCodecInfo codec in codecs)
{
if (codec.FormatID == ImageFormat.Jpeg.Guid)
{
var myEncoder = System.Drawing.Imaging.Encoder.Quality;
var myEncoderParameter = new EncoderParameter(myEncoder, 100L);
var myEncoderParameters = new EncoderParameters(1) { Param = { [0] = myEncoderParameter } };
newBmp.Save(#"C:\qqq\111.jpeg", codec, myEncoderParameters);
break;
}
}
Here is the MSDN article for it: https://msdn.microsoft.com/en-us/library/bb882583(v=vs.110).aspx
I have an image in an array and I'm saving it this way:
ImageConverter ic = new ImageConverter();
Image img = (Image)ic.ConvertFrom(Jpeg);
Bitmap bitmap1 = new Bitmap(img);
string saveString = "c:\\M_files\\new_pics\\" + pictureCounter + ".jpg";
bitmap1.Save(saveString, System.Drawing.Imaging.ImageFormat.Jpeg);
It works but I need it to be faster since its an image from camera that needs to stream. Is there a faster way? My array is in bytes then I use the container to convert to bitmap then save as jpeg.
try this code, this works fast and efficient, in this piece of code you can convert a picture to JPEG specitfying the width and height, and can pass as many number of images as you want. You can modify it to your own Requirement.
public void CreateThumbnail(string[] b, double wid, double hght, bool Isprint)
{
string[] path;
path = new string [64];
path = b;
string saveath = "i:\\check\\a test\\";
for (int i = 0; i < b.Length; i++)
{
DirectoryInfo dir = new DirectoryInfo(path[i]);
string dir1 = dir.ToString();
dir1 = dir1.Substring(dir1.LastIndexOf("\\"));
FileInfo[] files1 = dir.GetFiles();
foreach (FileInfo f in files1)
{
string gh = f.ToString();
try
{
System.Drawing.Image myThumbnail150;
System.Drawing.Image.GetThumbnailImageAbort myCallback = new System.Drawing.Image.GetThumbnailImageAbort(ThumbnailCallback);
System.Drawing.Image imagesize = System.Drawing.Image.FromFile(f.FullName);
Bitmap bitmapNew = new Bitmap(imagesize);
double maxWidth = wid;
double maxHeight = hght;
int w = imagesize.Width;
int h = imagesize.Height;
// Longest and shortest dimension
int longestDimension = (w > h) ? w : h;
int shortestDimension = (w < h) ? w : h;
// propotionality
float factor = ((float)longestDimension) / shortestDimension;
// default width is greater than height
double newWidth = maxWidth;
double newHeight = maxWidth / factor;
// if height greater than width recalculate
if (w < h)
{
newWidth = maxHeight / factor;
newHeight = maxHeight;
}
myThumbnail150 = bitmapNew.GetThumbnailImage((int)newWidth, (int)newHeight, myCallback, IntPtr.Zero);
string ext = Path.GetExtension(f.Name);
if (!Directory.Exists(saveath + dir1))
{
Directory.CreateDirectory(saveath + dir1);
myThumbnail150.Save(saveath + dir1 + "\\" + f.Name.Replace(ext, ".Jpeg"), System.Drawing.Imaging.ImageFormat.Jpeg);
}
else if(Directory.Exists(saveath+dir1))
{
myThumbnail150.Save(saveath + dir1+" \\"+ f.Name.Replace(ext, ".Jpeg"), System.Drawing.Imaging.ImageFormat.Jpeg);
}
}
catch (Exception ex)
{
Console.WriteLine("something went wrong" + ex.ToString());
}
}
}
}