Questo è un codice funzionale per ridurre un'immagine fino a una dimensione più piccola specificata. Ma ha diverse cose che non sono buone:Ridimensiona l'immagine JPEG alla dimensione specificata
- è lento
- può fare diverse iterazioni prima di ottenere l'immagine in scala
- ogni volta che deve determinare la dimensione che deve caricare l'intera immagine in un memoryStream
Mi piacerebbe migliorarlo. Ci può essere un modo per ottenere una stima iniziale migliore che precluda così tante iterazioni? Sto sbagliando tutto questo? Le mie ragioni per crearlo è accettare qualsiasi immagine di dimensioni sconosciute e ridimensionarla a una certa dimensione. Ciò consentirà una migliore pianificazione delle esigenze di archiviazione. Quando riduci a una certa altezza/larghezza, la dimensione dell'immagine può variare troppo per le nostre esigenze.
Avrete bisogno di un riferimento a System.Drawing.
//Scale down the image till it fits the given file size.
public static Image ScaleDownToKb(Image img, long targetKilobytes, long quality)
{
//DateTime start = DateTime.Now;
//DateTime end;
float h, w;
float halfFactor = 100; // halves itself each iteration
float testPerc = 100;
var direction = -1;
long lastSize = 0;
var iteration = 0;
var origH = img.Height;
var origW = img.Width;
// if already below target, just return the image
var size = GetImageFileSizeBytes(img, 250000, quality);
if (size < targetKilobytes * 1024)
{
//end = DateTime.Now;
//Console.WriteLine("================ DONE. ITERATIONS: " + iteration + " " + end.Subtract(start));
return img;
}
while (true)
{
iteration++;
halfFactor /= 2;
testPerc += halfFactor * direction;
h = origH * testPerc/100;
w = origW * testPerc/100;
var test = ScaleImage(img, (int)w, (int)h);
size = GetImageFileSizeBytes(test, 50000, quality);
var byteTarg = targetKilobytes * 1024;
//Console.WriteLine(iteration + ": " + halfFactor + "% (" + testPerc + ") " + size + " " + byteTarg);
if ((Math.Abs(byteTarg - size)/(double)byteTarg) < .1 || size == lastSize || iteration > 15 /* safety measure */)
{
//end = DateTime.Now;
//Console.WriteLine("================ DONE. ITERATIONS: " + iteration + " " + end.Subtract(start));
return test;
}
if (size > targetKilobytes * 1024)
{
direction = -1;
}
else
{
direction = 1;
}
lastSize = size;
}
}
public static long GetImageFileSizeBytes(Image image, int estimatedSize, long quality)
{
long jpegByteSize;
using (var ms = new MemoryStream(estimatedSize))
{
SaveJpeg(image, ms, quality);
jpegByteSize = ms.Length;
}
return jpegByteSize;
}
public static void SaveJpeg(Image image, MemoryStream ms, long quality)
{
((Bitmap)image).Save(ms, FindEncoder(ImageFormat.Jpeg), GetEncoderParams(quality));
}
public static void SaveJpeg(Image image, string filename, long quality)
{
((Bitmap)image).Save(filename, FindEncoder(ImageFormat.Jpeg), GetEncoderParams(quality));
}
public static ImageCodecInfo FindEncoder(ImageFormat format)
{
if (format == null)
throw new ArgumentNullException("format");
foreach (ImageCodecInfo codec in ImageCodecInfo.GetImageEncoders())
{
if (codec.FormatID.Equals(format.Guid))
{
return codec;
}
}
return null;
}
public static EncoderParameters GetEncoderParams(long quality)
{
System.Drawing.Imaging.Encoder encoder = System.Drawing.Imaging.Encoder.Quality;
//Encoder encoder = new Encoder(ImageFormat.Jpeg.Guid);
EncoderParameters eparams = new EncoderParameters(1);
EncoderParameter eparam = new EncoderParameter(encoder, quality);
eparams.Param[0] = eparam;
return eparams;
}
//Scale an image to a given width and height.
public static Image ScaleImage(Image img, int outW, int outH)
{
Bitmap outImg = new Bitmap(outW, outH, img.PixelFormat);
outImg.SetResolution(img.HorizontalResolution, img.VerticalResolution);
Graphics graphics = Graphics.FromImage(outImg);
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.DrawImage(img, new Rectangle(0, 0, outW, outH), new Rectangle(0, 0, img.Width, img.Height), GraphicsUnit.Pixel);
graphics.Dispose();
return outImg;
}
chiamata a questo creerà una seconda immagine che si avvicina per dimensioni al valore richiesto:
var image = Image.FromFile(@"C:\Temp\test.jpg");
var scaled = ScaleDownToKb(image, 250, 80);
SaveJpeg(scaled, @"C:\Temp\test_REDUCED.jpg", 80);
Per questo esempio specifico:
- dimensione del file originale: 628 kB
- dimensione file richiesta: 250 kB
- dimensioni file ridotte: 238 kB
Ho fatto qualcosa di simile al tuo suggerimento. Funziona, ma ci sono dei momenti in cui, ad esempio, la dimensione del target era 250 kB e l'immagine in scala era di 440 kB (orig size 628 kB) perché c'erano più dettagli nell'immagine o qualcosa del genere. Questo è troppo errore. Grazie per il suggerimento. – jbobbins