3

我有一个包含大约2500个PNG图像的文件夹,没有透明度。每个图像大约500 x 500(有些是491 x 433,其他511 x 499等)。如何避免在透明背景的调整大小的PNG中无用的白色边框?

我想以编程方式将每个图像缩小到其原始大小的10%,并将每个图像的白色背景设置为透明色。

为了测试我的应用程序的功能,而不是每次调整2500张图像的大小,我使用了15张台球图像作为“测试”文件夹。

现在我的问题是与下面的代码,我得到一个调整大小和裁剪PNG,whith 几乎透明背景。问题是每个图像查看器(Irfan View,Paint.Net和GIMP)都出现左边和上边的白色边框

如何避免此边框?

下面是我用这个代码:

void ResizeI(string[] Paths, string OutPut, Methods m, PointF Values, bool TwoCheck, bool Overwrite, float[] CropVals) 
    { 
     for (int i = 0; i < Paths.Length; i++)//Paths is the array of all images 
     { 
      string Path = Paths[i];//current image 
      Bitmap Oimg = (Bitmap)Bitmap.FromFile(Path);//original image 
      Bitmap img = new Bitmap((int)(Oimg.Width - CropVals[0] - CropVals[1]), (int)(Oimg.Height - CropVals[2] - CropVals[3]));//cropped image 
      Graphics ggg = Graphics.FromImage(img); 
      ggg.DrawImage(Oimg, new RectangleF(((float)-CropVals[0]), ((float)-CropVals[2]), Oimg.Width - CropVals[1], Oimg.Height - CropVals[3])); 
      ggg.Flush(System.Drawing.Drawing2D.FlushIntention.Flush); 
      ggg.Dispose(); 
      PointF scalefactor = GetScaleFactor(img, Values, TwoCheck);//the scale factor equals 0.1 for 10% 
      Bitmap newimg = new Bitmap((int)(Math.Ceiling(((float)img.Width) * scalefactor.X)), (int)(Math.Ceiling(((float)img.Height) * scalefactor.Y))); 
      System.Drawing.Imaging.ImageFormat curform = img.RawFormat; 
      string OutPath = System.IO.Path.Combine(OutPut, System.IO.Path.GetFileName(Path)); 
      OutPath = CheckPath(OutPath, Overwrite);//Delete if exsits 
      Graphics g = Graphics.FromImage(newimg); 
      g.InterpolationMode = GetModeFromMethod(m);//Bicubic interpolation 
      g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; 
      g.ScaleTransform(scalefactor.X, scalefactor.Y); 
      g.DrawImage(img, new Rectangle(0, 0, (int)Math.Ceiling(((float)newimg.Width)/scalefactor.X) + 1, (int)Math.Ceiling(((float)newimg.Height)/scalefactor.Y) + 1)); 
      //g.Flush(System.Drawing.Drawing2D.FlushIntention.Flush); 
      newimg.MakeTransparent(Color.White); 
      newimg.Save(OutPath, curform); 
      g.Dispose(); 
      img.Dispose(); 
     } 
    } 

这里是我提到的白色边框的例子。下载图像或拖动它,把一个黑色的背景下,它看到的边界:

The resized Image and the white border

- 编辑 -

我设法写这个函数,而不是newimg.MakeTransparent(...)

void SetTransparent(ref Bitmap b) 
    { 
     for (int i = 0; i < b.Width; i++) 
     { 
      for (int ii = 0; ii < b.Height; ii++) 
      { 
       Color cc = b.GetPixel(i, ii); 
       int tog = cc.R + cc.G + cc.B; 
       float durch = 255f - (((float)tog)/3f); 
       b.SetPixel(i, ii, Color.FromArgb((int)durch, cc.R, cc.G, cc.B)); 
      } 
     } 
    } 

的问题是,我的台球现在这个样子:

Second version of the billiard ball

回答

4

我不能帮助具体的代码,但也许可以解释发生了什么。

newimg.MakeTransparent(Color.White); 

这将采取一种颜色,并使其透明。值得注意的是,你的台球边缘(橙色)和纯白色背景之间有一系列的颜色。这是边缘的抗锯齿(这将是从球的纯橙色到背景纯白色的混合)。

通过转动只有纯白色透明,你仍然留下这个围绕对象的白色颜色的'晕'。

也许有更好的方法来处理这个使用白色值作为alpha蒙版,但我不确定.net的图像库是否可以处理(我将不得不推迟到有更多.net经验出现的人) 。

但是,在过渡期间,如果在调整大小之前设置透明度,可能会有所帮助。这不是一个真正的解决办法,但可能会减少一些光环效应。

UPDATE:

所以,我一直在思考这个多一些,我不能完全肯定有自动创建alpha通道透明度一个纲领性的解决方案,因为我有一种预感有很大的主观性参与其中。

关闭我的头顶,这是我想出了:

  • 假设左上角像素的100%透明色(我们会说像素X)。
  • 假设你希望你的透明背景是一个纯色(对比模式)
  • 承担大约3px的抗锯齿

然后你可以...

  • 支票与X相邻的像素。对于与X的颜色相匹配的X的每个相邻像素,我们将其设置为100%透明。
  • 如果x旁边的像素不相同,我们可以检查它是相对色调。
  • 从该像素分支并检查其周围的像素。
  • 这样做是为了标记每个像素(a,b,c等),直到相对色调改变一定的百分比和/或像素颜色与其邻居相同(具有一定的变化余量)。如果是这样,我们会假设我们很好地进入了对象的内部。
  • 通过您标记的像素现在倒退一步,调整透明度...例如c = 0%B = 33%= 66%

但尽管如此,这是一个什么样就真的要大过于简单化发生。它做了很多假设,没有考虑图案背景,并完全忽略了也需要透明的内部区域(如甜甜圈洞)。

通常在图形编辑应用程序中,这是通过选择背景颜色块,羽化所述选择的边缘,然后将其转换为最大alpha值来完成的。

这是一个非常有趣的问题/问题。我,唉,没有你的答案,但会好奇地看着这个线程!

+0

是的,我想到了,但我不知道如何通过.NET访问PNG的Alpha通道?顺便说一句,我的边界看起来像一些控件的3D边框... – alex 2011-06-04 11:32:25

+0

请参阅我的编辑... – alex 2011-06-04 18:07:50

+0

我不知道你可以做到这一点问题。挑战在于,您希望图像外部的白色透明,抗锯齿半透明,以及内部不透明。该脚本将不得不与一个特定的灰色区分开来,这个灰色是黑色抗锯齿边缘的一部分(您希望部分透明)与相同的灰色图像本身的纯色(你想要空白)。让我想一下,我会更新我的答案... – 2011-06-04 20:22:59

1

您编辑的SetTransparent功能是正确的方向,你几乎在那里。

只是轻微的修改,你可以试试这个:

void SetTransparent(ref Bitmap b)  
{ 
    const float selectivity = 20f; // set it to some number much larger than 1 but less than 255 
    for (int i = 0; i < b.Width; i++) 
    {   
     for (int ii = 0; ii < b.Height; ii++) 
     { 
      Color cc = b.GetPixel(i, ii); 
      float avgg = (cc.R + cc.G + cc.B)/3f; 
      float durch = Math.Min(255f, (255f - avgg) * selectivity); 
      b.SetPixel(i, ii, Color.FromArgb((int)durch, cc.R, cc.G, cc.B)); 
     } 
    } 
} 

的想法是,以避免影响弹子的Alpha值,你只会想减少是非常接近的颜色阿尔法零。换句话说,随着颜色离开白色,这是一个从0到255的快速上升的函数。

这将不会产生理想的结果,因为@DA表示,因为有一些信息丢失(透明像素和不透明像素在物体边缘附近混合在一起),这是不可恢复的。为了制作完美无混叠的Alpha边缘,必须使用透明度生成源图像本身。

+0

也许你是对的,但图像已经生成并且看起来非常类似于这些台球。我只是想,如果我能够在这15个不同的台球上自动处理透明度,那么代码也可以用于这1500个图像! – alex 2011-06-04 22:53:41

相关问题