我有 2 张图片,我需要将一张放在另一张上面。
第二张图像是在粉红色背景上拍摄的(如下所示),由于光线衰减,背景更像是渐变。
我需要将图像放在另一张图像上并删除背景颜色。我想定义一个代表我的背景的色相范围,并将属于该范围的每个像素删除/透明,以便将其粘贴在顶部,就好像它具有透明背景一样。
这是我想粘贴到任何随机图像上的示例图像:
我可以使用以下方法将图像粘贴到另一张图像上:
' Draw from the source to the destination.
gr.DrawImage(fr_bm, to_rect, fr_rect, GraphicsUnit.Pixel)
(图像,目标矩形,源矩形)
但我不知道如何删除背景。 非常感谢任何帮助。
这是一个标准的颜色替换过滤器(简化 -> 没有预卷积,因为您只想使颜色落在一定范围内的所有像素变得透明)。
它获取源图像,将其复制到 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