我正在使用GDI+来增强我的图像,当我使用Bitmap的MakeTransparent方法时,它确实起作用,并且确实执行了它应该执行的操作,但我仍然在整个图片上看到这些白色JPEG伪影。这些伪影使得图片质量很差,我最好不要使图片透明,只是保留白色,但这在我的网站上真的很丑陋。当然,我总是可以添加一个漂亮的边框和一个白色背景,但我更愿意将背景改为透明。
所以我想知道是否可以在C#中删除一些简单的JPEG伪影?有人做过吗?
感谢您的时间。
示例图片:
转换后的图像:
嗯,我尝试了一些远非完美的东西,但我想它可能对其他人有用。
我已经完成了:
遇到的问题:阴影距离“灰白色”足够远,难以自动转换,即使你这样做,阴影仍然会出现在图像本身上。 顶部反光...中心物体的光斑,也比抗锯齿位更接近灰白色。 图像中有三到七个白点,没有连接到任何主要角落; 最后边缘仍然有一点白色(可以通过调整代码来消除它,但是不能在不拆下反光顶部的一部分的情况下去除它)。
C# 低效的代码:
static void Main()
{
Bitmap bmp=new Bitmap("test.jpg");
int width = bmp.Width;
int height = bmp.Height;
Dictionary<Point, int> currentLayer = new Dictionary<Point, int>();
currentLayer[new Point(0, 0)] = 0;
currentLayer[new Point(width - 1, height - 1)] = 0;
while (currentLayer.Count != 0)
{
foreach (Point p in currentLayer.Keys)
bmp.SetPixel(p.X, p.Y, Color.Black);
Dictionary<Point, int> newLayer = new Dictionary<Point, int>();
foreach (Point p in currentLayer.Keys)
foreach (Point p1 in Neighbors(p, width, height))
if (Distance(bmp.GetPixel(p1.X, p1.Y), Color.White) < 40)
newLayer[p1] = 0;
currentLayer = newLayer;
}
bmp.Save("test2.jpg");
}
static int Distance(Color c1, Color c2)
{
int dr = Math.Abs(c1.R - c2.R);
int dg = Math.Abs(c1.G - c2.G);
int db = Math.Abs(c1.B - c2.B);
return Math.Max(Math.Max(dr, dg), db);
}
static List<Point> Neighbors(Point p, int maxX, int maxY)
{
List<Point> points=new List<Point>();
if (p.X + 1 < maxX) points.Add(new Point(p.X + 1, p.Y));
if (p.X - 1 >= 0) points.Add(new Point(p.X - 1, p.Y));
if (p.Y + 1 < maxY) points.Add(new Point(p.X, p.Y + 1));
if (p.Y - 1 >= 0) points.Add(new Point(p.X, p.Y - 1));
return points;
}
另一种基于评论对话框的方法:
static void Main()
{
Bitmap mask = new Bitmap(@"mask.bmp");
Bitmap bmp=new Bitmap(@"test.jpg");
int width = bmp.Width;
int height = bmp.Height;
for(int x=0; x<width; x++)
for (int y = 0; y < height; y++)
if (mask.GetPixel(x, y).R < 250)
bmp.SetPixel(x,y,mask.GetPixel(x,y));
bmp.Save(@"test3.jpg");
}
给定掩码:
你会得到结果:
使用Paint.NET稍微清理了口罩的边框,并禁用了抗锯齿。如果您可以辨别出使用了哪个边框,这仅适用于该边框...但它确实变得很好...除了绿色部分...
循环遍历图像中的每个像素,如果R、G和B大于230,则用所需的颜色(或透明)替换颜色。甚至可以根据旧颜色与“真正”白色相差多远来加权新颜色。
如果实际图像也是白色,则可能会出现问题,否则您将得到一个灰色的风暴兵 :)
你不可能用任何类似于100%准确度的东西自动完成此操作。
原因是您只有彩色信息,您知道图像中的一些像素正在尝试与其混合。仅有图像中的一些像素实际上会使用接近此值或相同值的颜色进行背景阴影处理,而其他像素将使用(在这种情况下为白色),因为实际上表示的对象是白色的(该帝国风暴军的精度真是够了)。
检测哪些是哪些的复杂机器学习是一个有趣的问题领域,可能会成为您的有趣项目,但肯定不会为您的直接问题提供快速解决方案。
您还面临另一个问题,即使您可以可靠地检测出图像中那些试图混合到背景中的区域,如果颜色不相容,则在将它们‘取消混合’并重新混合到新背景颜色中时会出现问题。在这种情况下,您的灰色可能有效,因为它是一种类似于白色的广谱颜色。
您想要使用的技术如下:
使用泛洪填充算法,从图像边缘向内选择所有距离已知背景颜色x%(1)以内的像素。 对于这些像素,将它们的alpha通道设置为与原始颜色匹配程度的比例值,并消除与之相关的色彩偏差。 因此,如果背景是RGB值a、b、c,像素是a+5、b、c-7,则结果为RGBA 5,0,0,((a+b+c-7)/(a+b+c)*256)(1)。 将这个alpha混合图像与新背景颜色的矩形合成。 将结果渲染为没有alpha通道的新图像。这只是一个非常粗略的第一次近似,但可能涵盖您目标的合理百分比。那些具有完全封闭透明孔洞的图片(例如您示例中外拱顶上的间隙)不太可能以自动方式良好地工作,因为算法将无法区分白色孔洞和白色风暴兵。
您可能希望使您的算法突出显示它计划重新混合的图片区域,并允许简单选择要包含/排除的区域(如果您想要花哨一些,可以使用Pain.Net的魔术棒选择工具作为示例来进行简单的像素选择,以减少前期工作量)。
感谢大家的回答,都是很好的建议。
这是我昨天想出来的:
public const int PIXEL_REGION = 60;
public const int TRANSPARENT_DISTANCE = 60;
public static readonly Color TRANSPARENT_COLOR = Color.White;
private System.Drawing.Image ProcessImage(System.Drawing.Image image)
{
Bitmap result = new Bitmap(image.Width, image.Height);
Bitmap workImage = new Bitmap(image);
for (int x = 0; x < image.Width; x++)
{
for (int y = 0; y < image.Height; y++)
{
Color color = workImage.GetPixel(x, y);
if (x < PIXEL_REGION || x > image.Width - PIXEL_REGION || y < PIXEL_REGION || y > image.Height - PIXEL_REGION)
{
double distance = CalculateColourDistance(TRANSPARENT_COLOR, color);
if(distance < TRANSPARENT_DISTANCE)
{
result.SetPixel(x, y, Color.Transparent);
continue;
}
}
result.SetPixel(x, y, color);
}
}
return result;
}
private double CalculateColourDistance(Color c1, Color c2)
{
int a = c2.A - c1.A;
int r = c2.R - c1.R;
int g = c2.G - c1.G;
int b = c2.B - c1.B;
return Math.Sqrt(Math.Pow(a, 2) + Math.Pow(r, 2) + Math.Pow(g, 2) + Math.Pow(b, 2));
}
虽然这不是书中最漂亮的代码,但它所做的事情是计算像素区域中每个像素与Color.White之间的距离,并且当距离小于预定义距离时,将其设为透明色。
这段代码产生以下结果:
不是很糟糕..但还是有点烂 :)
我还有一个绝招,如果成功了,我也会与你们分享...
你还需要处理一些类似于米白色的颜色。可能在最初制作图像并设置背景颜色时,进行了一些反锯齿处理,然后保存为jpg格式时,并不是所有颜色都能完美保留。因此,如果你想将某个特定颜色变成透明,它并不能获取该颜色的所有阴影,这会留下许多伪像。你需要使用一些方法,按照接近关键颜色的程度来进行透明度处理。这可能更容易通过Photoshop等软件批量处理,但我不知道你是否需要实时处理。
在编程方面,没有(远非简单的)方法来处理这个问题。图像边缘周围的白色伪影区域是几乎为白色但又不完全是白色的像素导致的,因此它们无法呈现透明效果。口罩/咖啡杯上还有一些纯白色斑点,因此它们变得透明并呈现灰色。
你最好联系原网站的管理员,看看他们能否以保留原始图层的格式将原始图像发送给你,希望是Photoshop或其他格式。然后,您可以以保持原始透明度(PNG或类似格式)或使用背景渐变重新生成图像(由于您不知道图片将呈现在渐变中的哪里,所以这将非常困难)。
如您所建议的那样,在图片周围加上某种边框。
这是又一次失败的尝试 ;)
我有一个想法.. 电影使用绿幕,为什么不为我的图像使用绿色叠加层.. 所以这就是叠加层:
结果如下:
所以我学到了有关PNG压缩的另一个宝贵经验.. 我也尝试了bmp,但不知怎么回事,总是出现绿色边框.. 我可以尝试擦除它,但也许我只需将其留在白色背景上,放置一些愚蠢的边框并完成它...
嗯,好吧.. ;)