2017-06-26 3 views
0

Je ne pouvais pas m'en empêcher, encore une fois je vous demande de l'aide. Cette fois, je vais montrer le problème mieux que la dernière fois, j'espère. J'écris un programme pour vérifier si la quantification a une influence sur les tailles d'image. Pour ce faire, je dois avoir mis en œuvre:C# PNG Sauvegarde de l'image avec filtre sélectionné

  1. Ouvrir une image PNG (fait)
  2. "Quantifier" pixel par pixel jusqu'à la fin de l'image (DONE)
  3. Enregistrer (ce est le problème)

méthode du filtre PNG 0 définit cinq types de filtres de base: type Nom

0 - Aucun, 1 - Sub, 2 -, 3 pages - moyenne, 4 - Paeth

Et maintenant, je suis debout avec une image en mémoire que je veux sauver en utilisant l'un des filtres qui , mais après avoir vérifié plusieurs bibliothèques PNG, aucune d'entre elles ne me permet d'en choisir une. Quelqu'un peut-il m'aider avec cela ou au moins avec un filtre? Ici vous allez avec un code:

private void btnSelectImg_Click(object sender, EventArgs e) 
    { 
     openFileDialog1.Filter = "PNG Image | *.png"; 
     DialogResult result = openFileDialog1.ShowDialog(); 

     if (result == DialogResult.OK) 
     { 

      string imgPath = openFileDialog1.FileName; 

      tbSourceImageFile.Text = imgPath; 
      string[] NameCutter = imgPath.Split('\\'); 
      lblFileName.Text = NameCutter.Last(); 

      ImageToWork = Image.FromFile(imgPath); 
      System.Drawing.Imaging.ImageFormat Format = ImageToWork.RawFormat; 
      tbInfo.Text += string.Format("Resolution : {0}x{1} | Bits : {2:n0} | Format : {3}", ImageToWork.Width, ImageToWork.Height, ImageToWork.Width * ImageToWork.Height, GetFilenameExtension(Format)); 


     } 

    } 
private void btnSave_Click(object sender, EventArgs e) 
    { 
     #region Check Image 
     if (tbSourceImageFile.Text == "") 
     { 
      MessageBox.Show("File not selected. Select file first."); 
      return; 
     } 
     #endregion 
     #region Operations on image 

     Bitmap Image111 = new Bitmap(tbSourceImageFile.Text, true); 

     #region Progress Bar Settings 
     ProgressBar.Visible = true; 
     ProgressBar.Value = 1; 
     ProgressBar.Maximum = Image111.Width; 
     ProgressBar.Step = 1; 
     #endregion 
     if (cboxEnableScale.Checked == true) 
     { 
      int red, green, blue, red2=0, blue2=0, green2=0; 
      int scale = int.Parse(cbSelectorScale.SelectedItem.ToString()); 
      for (int w = 0; w < Image111.Width; w++) 
      { 
       for (int h = 0; h < Image111.Height; h++) 
       { 
        Color PixelColor = Image111.GetPixel(w, h); 
        #region Quantization 

        red = PixelColor.R; 
        green = PixelColor.G; 
        blue = PixelColor.B; 

        Color newColor = Color.FromArgb(Valuator_v3(red, scale), Valuator_v3(green, scale), Valuator_v3(blue, scale)); 
        Image111.SetPixel(w, h, newColor); 

        #endregion 
       } 
       ProgressBar.PerformStep(); 
      } 
     } 
     #endregion 


     #region Saving 
     string SaveDirectory = tbSaveDestination.Text + '\\' + tbSaveFileName.Text + ".bmp"; 

     SaveDirectory = tbSaveDestination.Text + '\\' + tbSaveFileName.Text + ".jpeg"; 

     Image111.Save(SaveDirectory, System.Drawing.Imaging.ImageFormat.Png); 

     ProgressBar.Visible = false; 
     MessageBox.Show("Saved successfully."); 

     #endregion 
    } 

Dans la région « Enregistrement » Je veux choisir quel filtre sera utilisé et de l'enregistrer à l'utiliser.

+0

Un bon point de départ pourrait être de l'enregistrer * comme * un PNG en premier lieu plutôt qu'un Jpeg? 'Image111.Save (SaveDirectory, ImageFormat.Jpeg);'? Ensuite, vous appelez 'Save' une seconde fois et ne passez que le répertoire? – sab669

+0

@ sab669 Oh, je faisais des tests et j'ai oublié de le changer. Bien sûr, enregistrer cette image ressemble à: 'Image111.Save (SaveDirectory, System.Drawing.Imaging.ImageFormat.Png);' –

+0

Il y a un encodeur PNG [ici] (https://github.com/oltur/seeyourtravel.com/ blob/master/tools/WicResize/ThumbnailPNG.ashx) qui a [ces options de filtre] (https://github.com/oltur/seeyourtravel.com/blob/master/tools/WicResize/InteropServices/ComTypes/WinCodec.cs# L2409) – stuartd

Répondre

0

mais après avoir vérifié plusieurs des bibliothèques PNG, aucun d'entre eux me permettent de choisir un

Avez-vous essayé PngCs? (il y a plusieurs fourchettes là-bas). Voir PngWriter. FilterWriteStrategy(...) méthode

0

Si les bibliothèques PNG ne font pas ce que vous voulez, lancez simplement vos propres filtres. Ce n'est pas si difficile.

Le filtrage doit avoir lieu à l'intérieur de la quantification #region. Dans la mesure où je le décompresse, la méthode Valuator_v3() convertit les canaux RVB séparément, puis vous stockez le pixel transformé avec Image111.SetPixel(). Le filtre PNG doit être inséré entre les deux appels.

Les filtres PNG fonctionnent sur la couleur de pixel actuelle et sur un, deux ou trois pixels voisins lus précédemment. Ils ne regardent jamais en avant. Vous utilisez simplement Image111.GetPixel() pour récupérer les pixels précédents et les utiliser pour transformer le pixel courant. Dans le cas du type de filtre "None", il n'y a rien à faire, et vous ne stockez que le pixel quantifié.

Dans le cas de "Sub", vous testez si vous êtes dans la colonne la plus à gauche (c'est-à-dire, w == 0). Si c'est le cas, vous laissez le pixel tel quel. Dans le cas contraire, vous appelez Image111.GetPixel (w-1, h) et les valeurs soustrayez RVB résultant du pixel courant:

Color pixelLeft = Image111.GetPixel (w-1, h); 
newColor.R -= pixelLeft.R; 
newColor.G -= pixelLeft.G; 
newColor.B -= pixelLeft.B; 

Voilà. La transformation "Up" est également triviale. Vous vérifiez simplement h == 0 cette fois, et appelez Image111.GetPixel (w, h-1) si le pixel courant n'est pas dans la rangée supérieure. Le filtre "moyen" requiert à la fois les pixels gauche et supérieur, et calcule la moyenne arithmétique des valeurs de canal RVB.Notez que pixelLeft = 0 en cas de w == 0, et pixelTop = 0 en cas de h == 0:

Color pixelLeft = Image111.GetPixel (w-1, h); 
Color pixelTop = Image111.GetPixel (w, h-1); 
newColor.R -= (Byte) (((UInt64) pixelLeft.R + (UInt64) pixelTop.R) >> 1); 
newColor.G -= (Byte) (((UInt64) pixelLeft.G + (UInt64) pixelTop.G) >> 1); 
newColor.B -= (Byte) (((UInt64) pixelLeft.B + (UInt64) pixelTop.B) >> 1); 

Le filtre Paeth est plus complexe. Il utilise trois pixels voisins, pixelLeft, pixelTop et pixelTopLeft. Encore une fois, vous devez vérifier les cas de frontière spéciaux de manière appropriée. Le prédicteur suivant est calculé séparément pour chaque canal, par ex. rouge:

Int64 valueLeft  = pixelLeft.R; 
Int64 valueTop  = pixelTop.R; 
Int64 valueTopLeft = pixelTopLeft.R; 
Int64 valueCombined = valueLeft + valueTop - valueTopLeft; 

Int64 deltaLeft  = Math.Abs (valueCombined - valueLeft); 
Int64 deltaTop  = Math.Abs (valueCombined - valueTop); 
Int64 deltaTopLeft = Math.Abs (valueCombined - valueTopLeft); 

newColor.R -= (deltaLeft <= deltaTop) && (deltaLeft <= deltaTopLeft) 
       ? pixelLeft.R 
       : deltaTop <= deltaTopLeft ? pixelTop.R : pixelTopLeft.R); 

Bien que le filtre Paeth semble assez prometteur, mes propres tests ont montré que le filtre « Up » donne les meilleurs résultats dans la plupart des cas. Je ne sais pas pourquoi. Donc, par défaut, j'utilise le filtre "Sub" pour la première ligne, et le filtre "Up" pour tous les suivants.

Vous avez maintenant l'image filtrée en mémoire. Ce dont vous avez besoin maintenant, c'est d'un encodeur DEFLATE standard, comme ZLIB l'utilise. L'entrée du codeur correspond aux données RVB filtrées. Notez que PNG vous oblige à émettre le type de filtre (0..4) en tant que code littéral au début de chaque ligne. Le flux DEFLATE compressé est empaqueté dans un bloc IDAT d'un conteneur PNG, ce qui n'est pas une tâche difficile.