将图像放在另一个图像之上,使渐变背景颜色透明

问题描述 投票:0回答:1

我有 2 张图片,我需要将一张放在另一张上面。
第二张图像是在粉红色背景上拍摄的(如下所示),由于光线衰减,背景更像是渐变。

我需要将图像放在另一张图像上并删除背景颜色。我想定义一个代表我的背景的色相范围,并将属于该范围的每个像素删除/透明,以便将其粘贴在顶部,就好像它具有透明背景一样。

这是我想粘贴到任何随机图像上的示例图像:

我可以使用以下方法将图像粘贴到另一张图像上:

' Draw from the source to the destination.
gr.DrawImage(fr_bm, to_rect, fr_rect, GraphicsUnit.Pixel)

(图像,目标矩形,源矩形)

但我不知道如何删除背景。 非常感谢任何帮助。

vb.net graphics background gdi+ transparent
1个回答
2
投票

这是一个标准的颜色替换过滤器(简化 -> 没有预卷积,因为您只想使颜色落在一定范围内的所有像素变得透明)。
它获取源图像,将其复制到 32 位 ARGB 位图,然后生成相同的容器,用作目标位图。

所有颜色都会与

colorFrom
参数中指定的颜色进行比较,如果颜色的分量在
tolerance
参数定义的阈值内,则颜色将替换为
colorTo
参数中指定的颜色。

tolerance
值应在
(1:100)
范围内(只是因为Photoshop和其他图形程序这样做),
ColorReplacement
方法会自行标准化该值。

可能的结果
对于示例中的图像,将

colorFrom
设置为
Color.Fucsia
并将
colorTo
设置为
Color.Transparent
,绿色区域以
~56
的公差被隔离,然后外部颜色的所有剩余痕迹消失(沿着与任何抗锯齿),在
80
90
之间。之后,绿色区域也开始消失。 95左右,你就有了一个完全透明的位图。

colorFrom
设置为
(255, 226, 18, 212)
时,相同的结果出现在
~38
,然后
60
70
(替换更加 微妙)。
这意味着您必须选择一种在您的视图和上下文中能够提供更好结果的源颜色。

尝试向该方法传递不同的值。

C#版本:

public Bitmap ColorReplacement(Bitmap image, Color colorFrom, Color colorTo, float tolerance)
{
    tolerance = (byte)(255.0f / 100.0f * Math.Max(Math.Min(100.0f, tolerance), 0.1f));
    Bitmap source = new(image.Width, image.Height, PixelFormat.Format32bppArgb);
    source.SetResolution(image.HorizontalResolution, image.VerticalResolution);
    using (var g = Graphics.FromImage(source)) {
        g.PixelOffsetMode = PixelOffsetMode.Half;
        g.DrawImage(image, Point.Empty);
    }

    Bitmap destImage = new(source.Width, source.Height, PixelFormat.Format32bppArgb);
    destImage.SetResolution(image.HorizontalResolution, image.VerticalResolution);
    foreach (PropertyItem item in image.PropertyItems) {
        source.SetPropertyItem(item);
        destImage.SetPropertyItem(item);
    }

    var dataFrom = source.LockBits(new Rectangle(0, 0, source.Width, source.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
    var dataTo = destImage.LockBits(new Rectangle(0, 0, destImage.Width, destImage.Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);

    byte[] buffer = new byte[Math.Abs(dataTo.Stride) * dataTo.Height];
    Marshal.Copy(dataFrom.Scan0, buffer, 0, buffer.Length);

    source.UnlockBits(dataFrom);
    int bytesPerPixel = Image.GetPixelFormatSize(source.PixelFormat) / 8;

    for (int pos = 0; pos < buffer.Length; pos += bytesPerPixel) {
        byte blue = buffer[pos];
        byte green = buffer[pos + 1];
        byte red = buffer[pos + 2];

        if ((blue < colorFrom.B + tolerance && blue > colorFrom.B - tolerance) &&
            (green < colorFrom.G + tolerance && green > colorFrom.G - tolerance) &&
            (red < colorFrom.R + tolerance && red > colorFrom.R - tolerance)) {

            int newBlue = colorFrom.B - blue + colorTo.B;
            int newGreen = colorFrom.G - green + colorTo.G;
            int newRed = colorFrom.R - red + colorTo.R;

            buffer[pos] = (byte)Math.Max(Math.Min(255, newBlue), 0);
            buffer[pos + 1] = (byte)Math.Max(Math.Min(255, newGreen), 0);
            buffer[pos + 2] = (byte)Math.Max(Math.Min(255, newRed), 0);
            buffer[pos + 3] = colorTo.A;
        }
    }
    Marshal.Copy(buffer, 0, dataTo.Scan0, buffer.Length);
    destImage.UnlockBits(dataTo);
    return destImage;
}

VB.NET版本:

Public Shared Function ColorReplacement(imageSource As Bitmap, colorFrom As Color, colorTo As Color, tolerance As Single) As Bitmap
    tolerance = CByte(255.0F / 100.0F * Math.Max(Math.Min(100.0F, tolerance), 0.1F))

    Dim source As New Bitmap(imageSource.Width, imageSource.Height, PixelFormat.Format32bppArgb)
    source.SetResolution(imageSource.HorizontalResolution, imageSource.VerticalResolution)
    Using g = Graphics.FromImage(source)
        g.PixelOffsetMode = PixelOffsetMode.Half
        g.DrawImage(imageSource, Point.Empty)
    End Using

    Dim destImage As Bitmap = New Bitmap(source.Width, source.Height, PixelFormat.Format32bppArgb)
    destImage.SetResolution(imageSource.HorizontalResolution, imageSource.VerticalResolution)
    For Each item As PropertyItem In imageSource.PropertyItems
        source.SetPropertyItem(item)
        destImage.SetPropertyItem(item)
    Next

    Dim dataFrom = source.LockBits(New Rectangle(0, 0, source.Width, source.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb)
    Dim dataTo = destImage.LockBits(New Rectangle(0, 0, destImage.Width, destImage.Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb)

    Dim buffer As Byte() = New Byte(Math.Abs(dataFrom.Stride) * dataFrom.Height - 1) {}
    Marshal.Copy(dataFrom.Scan0, buffer, 0, buffer.Length)

    source.UnlockBits(dataFrom)
    Dim bytesPerPixel As Integer = Image.GetPixelFormatSize(source.PixelFormat) \ 8

    For pos As Integer = 0 To buffer.Length - 1 Step bytesPerPixel
        Dim blue As Integer = buffer(pos)
        Dim green As Integer = buffer(pos + 1)
        Dim red As Integer = buffer(pos + 2)

        If (blue < colorFrom.B + tolerance AndAlso blue > colorFrom.B - tolerance) AndAlso
            (green < colorFrom.G + tolerance AndAlso green > colorFrom.G - tolerance) AndAlso
            (red < colorFrom.R + tolerance AndAlso red > colorFrom.R - tolerance) Then

            Dim newBlue As Integer = colorFrom.B - blue + colorTo.B
            Dim newGreen As Integer = colorFrom.G - green + colorTo.G
            Dim newRed As Integer = colorFrom.R - red + colorTo.R

            buffer(pos) = CByte(Math.Max(Math.Min(255, newBlue), 0))
            buffer(pos + 1) = CByte(Math.Max(Math.Min(255, newGreen), 0))
            buffer(pos + 2) = CByte(Math.Max(Math.Min(255, newRed), 0))
            buffer(pos + 3) = colorTo.A
        End If
    Next
    Marshal.Copy(buffer, 0, dataTo.Scan0, buffer.Length)
    destImage.UnlockBits(dataTo)
    Return destImage
End Function
© www.soinside.com 2019 - 2024. All rights reserved.