使用所选过滤器保存C#PNG图像

时间:2017-06-26 16:49:09

标签: c# filter save png quantization

我无法自拔,所以我再次请求你的帮助。这次我希望能比上次更好地展示问题。我希望。

我正在编写一个程序来检查量化是否对图像大小有任何影响。要做到这一点,我需要实施:

  1. 打开PNG图像(已完成)
  2. 逐像素地“量化”直到图像的结尾(已完成)
  3. 保存(这是问题)
  4.   

    PNG过滤器方法0定义了五种基本过滤器类型:类型名称

         

    0 - 无,1 - Sub,2 - Up,3 - Average,4 - Paeth

    现在,我站在内存中的图像,我想使用其中一个过滤器保存,但在检查了多个PNG库之后,没有一个允许我选择一个。任何人都可以帮助我或至少使用一个过滤器? 在这里你可以使用一些代码:

    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
        }
    

    在区域“保存”中我想选择使用哪个过滤器并使用它保存。

2 个答案:

答案 0 :(得分:0)

  

但在检查了多个PNG库之后,其中没有一个允许我选择一个

你试过PngCs吗? (那里有几把叉子)。请参阅PngWriter. FilterWriteStrategy(...)方法

答案 1 :(得分:0)

如果PNG图书馆没有做你想做的事情,只需滚动自己的过滤器。这并不困难。

过滤应在#region量化中进行。据我所知,Valuator_v3()方法分别转换RGB通道,然后用Image111.SetPixel()存储转换后的像素。需要在两次调用之间插入PNG过滤器。

PNG滤镜处理当前像素颜色和一个,两个或三个先前读取的相邻像素。他们从不向前看。因此,您只需使用Image111.GetPixel()来检索以前的像素,并使用它们来转换当前像素。对于过滤器类型&#34;无&#34;,没有任何关系,你只需存储量化像素。

在&#34; Sub&#34;的情况下,您测试您是否在最左边的列中(即,w == 0)。如果是这样,则保持像素不变。否则,您调用Image111.GetPixel(w-1,h)并从当前像素中减去生成的RGB值:

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

那就是它。 &#34; Up&#34;变换同样微不足道。你这次只检查h == 0,如果当前像素不在顶行,则调用Image111.GetPixel(w,h-1)。 &#34;平均&#34;滤波器需要左上像素和上像素,并计算RGB通道值的算术平均值。注意,在w == 0的情况下pixelLeft = 0,在h == 0的情况下pixelTop = 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);

Paeth过滤器更复杂。它使用三个相邻像素,pixelLeft,pixelTop和pixelTopLeft。您需要再次适当地检查特殊边界情况。对于每个信道,单独计算以下预测值,例如,红色:

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);

尽管Paeth过滤器看起来非常有前景,但我自己的测试表明,&#34; Up&#34;在大多数情况下,过滤器产生最佳结果。不知道为什么。因此,默认情况下,我使用&#34; Sub&#34;过滤第一行,&#34; Up&#34;过滤所有后续的。

所以现在你已经在内存中获得了过滤后的图像。您现在需要的是标准的DEFLATE编码器,就像ZLIB一样。编码器输入是滤波后的RGB数据。请注意,PNG要求您在每行的开头发出过滤器类型(0..4)作为文字代码。 压缩的DEFLATE流被打包到PNG容器的IDAT块中,这不是一项艰巨的任务。