2011-01-21 14 views
7

Ho la seguente funzione che viene chiamata per cambiare la risoluzione di un'immagine. Voglio fare questa immagine così caricata con per esempio 300 dpi saranno modificati a 72 dpi (per il web). Questa domanda è correlata a another question qui su SO in cui sto lavorando.C# cambia dpi di un'immagine caricata

Sto creando un metodo di estensione per questo per poter utilizzare questa funzione su più punti della mia applicazione, invece di caricare solo nuovi file. (Vedere la domanda precedente)

public static byte[] SetDpiTo72(this byte[] imageToFit, string mimeType, Size newSize) 
{  
    using (MemoryStream memoryStream = new MemoryStream(), newMemoryStream = new MemoryStream()) 
    { 
     memoryStream.Write(imageToFit, 0, imageToFit.Length); 
     var originalImage = new Bitmap(memoryStream); 

     using (var canvas = Graphics.FromImage(originalImage)) 
     { 
      canvas.SmoothingMode = SmoothingMode.AntiAlias; 
      canvas.InterpolationMode = InterpolationMode.HighQualityBicubic; 
      canvas.PixelOffsetMode = PixelOffsetMode.HighQuality; 
      canvas.DrawImage((Image)originalImage,0,0, newSize.Width, newSize.Height); 

      newBitmap.SetResolution(72, 72); 
      newBitmap.Save(newMemoryStream, ImageFunctions.GetEncoderInfo(mimeType), null); 
     } 
     return newMemoryStream.ToArray(); 
    } 
} 

Il metodo di estensione menzionato viene chiamato in una funzione simile alla situazione seguente;

if (newSize.Width > originalImage.Width && newSize.Height > originalImage.Height) 
{ 
    newSize.Width = originalImage.Width; 
    newSize.Height = originalImage.Height; 

    uploadedFileBuffer = uploadedFileBuffer.SetDpiTo72(uploadedFile.ContentType, newSize); 

    return CreateFile(newSize, uploadedFile, uploadedFileBuffer); 
} 

ByteArray in arrivo è il file come un ByteArray. Ha già la dimensione corretta, ma voglio cambiare la risoluzione a 72 dpi. Tuttavia, dopo l'esecuzione e il salvataggio dell'immagine, la risoluzione è ancora la risoluzione inserita originale, che è 300 dpi. Come posso fare questo?

AGGIORNAMENTO DOPO PIU 'RISPOSTE:

public static byte[] SetDpiTo72(this byte[] imageToFit, string mimeType, Size newSize) 
     { 
      using (MemoryStream memoryStream = new MemoryStream(), newMemoryStream = new MemoryStream()) 
      { 
       memoryStream.Write(imageToFit, 0, imageToFit.Length); 
       var originalImage = new Bitmap(memoryStream); 

       using (var canvas = Graphics.FromImage(originalImage)) 
       { 
        canvas.SmoothingMode = SmoothingMode.AntiAlias; 
        canvas.InterpolationMode = InterpolationMode.HighQualityBicubic; 
        canvas.PixelOffsetMode = PixelOffsetMode.HighQuality; 
        canvas.DrawImage((Image)originalImage,0,0, newSize.Width, newSize.Height); 

        originalImage.SetResolution(72, 72); 

        var epQuality = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 75); 
        var epParameters = new EncoderParameters(1); 
        epParameters.Param[0] = epQuality; 

        Image newimg = Image.FromStream(memoryStream); 

        //Getting an GDI+ exception after the execution of this line. 
        newimg.Save("C:\\test1234.jpg", ImageFunctions.GetEncoderInfo(mimeType), epParameters); 

        originalImage.Save("test.jpg", ImageFormat.Jpeg); 

        //This line give me an Argumentexception - Parameter is not valid. 
        //originalImage.Save(newMemoryStream, ImageFunctions.GetEncoderInfo(mimeType), epParameters); 
        //newMemoryStream.Close(); 
       } 
       return newMemoryStream.ToArray(); 
      } 
     } 

Lo stackstrace che viene fornito con l'eccezione me il seguente sta dicendo;

at System.Drawing.Image.Save(String filename, ImageCodecInfo encoder, EncoderParameters encoderParams) 
    at Extensions.ByteArrayExtensions.SetDpiTo72(Byte[] imageToFit, String mimeType, Size newSize) in C:\Website\Project\Extensions\ByteArrayExtensions.cs:line 356 
    at CMS.Presentation.FileFunctions.CreateFullsizeImage(HttpPostedFileBase uploadedFile, Size newSize, Byte[] uploadedFileBuffer) in C:\Website\Project\CMS.Presentation\FileFunctions.cs:line 197 
    at CMS.Presentation.FileFunctions.CreateFile(HttpPostedFileBase uploadedFile, INodeService nodeservice, Guid userId, Node parentNode) in C:\Website\Project\CMS.Presentation\FileFunctions.cs:line 53 

Nel frattempo ho anche sviluppato un'altra funzione (vedi sotto) ridimensionando solo una bitmap. E questo sembra funzionare correttamente. Non posso usare questa funzione con la mia attuale implementazione sebbene restituisca solo una Bitmap. O dovrei cambiare tutto per lavorare con le bitmap?

private static Bitmap ResizeImage(Image image, int width, int height) 
     { 
      var frameCount = image.GetFrameCount(new FrameDimension(image.FrameDimensionsList[0])); 
      var newDimensions = ImageFunctions.GenerateImageDimensions(image.Width, image.Height, width, height); 
      Bitmap resizedImage; 

      if (frameCount > 1) 
      { 
       //we have a animated GIF 
       resizedImage = ResizeAnimatedGifImage(image, width, height); 
      } 
      else 
      { 
       resizedImage = (Bitmap)image.GetThumbnailImage(newDimensions.Width, newDimensions.Height, null, IntPtr.Zero); 
      } 

      resizedImage.SetResolution(72,72); 

      return resizedImage; 
     } 
+1

Sai che hai un bel codice circolare lì? newBitmap = originalImage NON COPIA. È solo un riferimento alla stessa immagine. È divertente come possa ancora funzionare. – Euphoric

+0

haha ​​buono! È cambiato ;-) – Rob

+0

Aspetta .. ha effettivamente risolto il tuo problema? – Euphoric

risposta

3

ho messo un po ', ma alla fine ho trovato il problema! Il problema è stato risolto nella funzione ResizeImage che ho usato. In 'GetThumbnailImage' per essere specifici. Mi sono imbattuto in un altro problema con immagini sfocate, che è stato spiegabile perché GetThumbnailImage avrebbe allungato il ThumbNail creato alla dimensione desiderata. E la risoluzione della miniatura non cambia mai.

private static Bitmap ResizeImage(Image image, int width, int height) 
     { 
      var frameCount = image.GetFrameCount(new FrameDimension(image.FrameDimensionsList[0])); 
      var newDimensions = ImageFunctions.GenerateImageDimensions(image.Width, image.Height, width, height); 
      Bitmap resizedImage; 

      if (frameCount > 1) 
      { 
       //we have a animated GIF 
       resizedImage = ResizeAnimatedGifImage(image, width, height); 
      } 
      else 
      { 
       resizedImage = (Bitmap)image.GetThumbnailImage(newDimensions.Width, newDimensions.Height, null, IntPtr.Zero); 
      } 

      resizedImage.SetResolution(72,72); 

      return resizedImage; 
     } 

Modificando la funzione sopra la seguente funzione ho potuto risolvere il problema utilizzando Graphics.DrawImage ridisegnare la nuova immagine prima del rendering esso. Anche GenerateImageDimensions è stato leggermente modificato. Questo insieme ha risolto il problema.

private static Bitmap ResizeImage(Image image, int width, int height) 
     { 
      var frameCount = image.GetFrameCount(new FrameDimension(image.FrameDimensionsList[0])); 
      var newDimensions = ImageFunctions.GenerateImageDimensions(image.Width, image.Height, width, height); 

      var resizedImage = new Bitmap(newDimensions.Width, newDimensions.Height); 
      if (frameCount > 1) 
      { 
       //we have a animated GIF 
       resizedImage = ResizeAnimatedGifImage(image, width, height); 
      } 
      else 
      { 

       //we have a normal image 
       using (var gfx = Graphics.FromImage(resizedImage)) 
       { 
        gfx.SmoothingMode = SmoothingMode.HighQuality; 
        gfx.InterpolationMode = InterpolationMode.HighQualityBicubic; 
        gfx.PixelOffsetMode = PixelOffsetMode.HighQuality; 

        var targRectangle = new Rectangle(0, 0, newDimensions.Width, newDimensions.Height); 
        var srcRectangle = new Rectangle(0, 0, image.Width, image.Height); 

        gfx.DrawImage(image, targRectangle, srcRectangle, GraphicsUnit.Pixel); 
       } 
      } 

      return resizedImage; 
     } 
1

Con il termine "modifica della risoluzione", si fa realmente dire che si desidera ridurre il numero di pixel nelle immagini di 72/300? Cioè cambia un'immagine di 4000x3000 a 960x720?

Se è così, non riesco a vedere dove il codice lo fa effettivamente. Lo overload of DrawImage() you're using esegue questa operazione:

Disegna l'immagine specificata, utilizzando la sua dimensione fisica originale, nella posizione specificata da una coppia di coordinate.

Che è esattamente ciò che sta accadendo.

Prova one of the other overloads quali this one: richiama l'immagine specificata nella posizione specificata e con le dimensioni specificate

.

ad esempio:

// Create image. 
Image newImage = Image.FromFile("SampImag.jpg"); 

// Create coordinates for upper-left corner of image and for size of image. 
int x = 0; 
int y = 0; 
int width = 450; 
int height = 150; 

// Draw image to screen. 
e.Graphics.DrawImage(newImage, x, y, width, height); 

EDIT: per i commenti, ho capito il PO vuole ridurre le dimensioni del file senza ridurre numero di pixel. Pertanto i file devono essere ricompressi.

Ho preso in prestito un po 'di codice di esempio da here:

ImageCodecInfo iciJpegCodec = null; 

// This will specify the image quality to the encoder. Change the value of 75 from 0 to 100, where 100 is best quality, but highest file size. 
EncoderParameter epQuality = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 75); 

// Get all image codecs that are available 
ImageCodecInfo[] iciCodecs = ImageCodecInfo.GetImageEncoders(); 

// Store the quality parameter in the list of encoder parameters 
EncoderParameters epParameters = new EncoderParameters(1); 
epParameters.Param[0] = epQuality; 

// Loop through all the image codecs 
for (int i = 0; i < iciCodecs.Length; i++) 
{ 
    // Until the one that we are interested in is found, which is image/jpeg 
    if (iciCodecs[i].MimeType == "image/jpeg") 
    { 
     iciJpegCodec = iciCodecs[i]; 
     break; 
    } 
} 

// Create a new Image object from the current file 
Image newImage = Image.FromFile(strFile); 

// Get the file information again, this time we want to find out the extension 
FileInfo fiPicture = new FileInfo(strFile); 

// Save the new file at the selected path with the specified encoder parameters, and reuse the same file name 
newImage.Save(outputPath + "\\" + fiPicture.Name, iciJpegCodec, epParameters); 
+0

Quello che voglio davvero è la riduzione del numero di pixel per pollice nell'immagine. Per esempio se carico un'immagine con la dimensione di 1181 x 1181 in 300 dpi voglio che venga salvata con la dimensione originale di 1181 x 1181 buth con 72 dpi anziché l'originale 300 – Rob

+0

Ho appena visto che non stavo dando l'immagine richiesta dimensione quando si chiama la funzione drawimage. Proverai a modificare questo .. – Rob

+0

Ah, riducendo il valore DPI nel file, piuttosto che il numero totale effettivo di pixel nell'immagine. Scusa, non sono proprio sicuro di quale sia il problema allora. Tieni presente che il valore di "risoluzione" salvato nel file immagine non ha davvero alcun concetto significativo quando l'immagine è archiviata in modo digitale - questo entra in gioco solo quando l'immagine viene riprodotta - sei sicuro di aver effettivamente bisogno di farlo? – tomfanning

0

Rob, credo che problema con il codice è di salvare l'immagine - i dati effettivi di immagine digitale sarebbe certo numero di punti/pixel vale a dire (mxn) e l'impostazione della risoluzione su bitmap non dovrebbe/non dovrebbe cambiare i punti numerici (e quindi la dimensione fisica dei byte dell'immagine). Le informazioni di risoluzione saranno memorizzate nell'intestazione dell'immagine (per essere utilizzato da programmi mentre le immagini stampa/editing) - cosa succede se si memorizza il nuovo bitmap al file invece di mem streaming

newBitmap.Save("c:\test.png", ImageFormat.Png); 

Controllare dpi per file di cui sopra da file -> proprietà -> riepilogo (avanzato). Dovrebbe essere 72 dpi.

+0

Proverà il rifiuto di salvarlo sul file system. Ma questa non può essere una soluzione finale. Ne ho bisogno per essere salvato nel database alla fine perché fuori dallo sviluppo dell'applicazione – Rob

+0

Ho provato il tuo suggerimento ma sto ricevendo un'eccezione GDI +. Ho aggiornato la domanda – Rob

2

Ok, l'ho provato solo sui file su hard disk, ma dovrebbe funzionare anche con gli stream.

 Bitmap bitmap = new Bitmap(loadFrom); 
     Bitmap newBitmap = new Bitmap(bitmap); 
     newBitmap.SetResolution(72, 72); 
     newBitmap.Save(saveTo); 
Problemi correlati