如何读取非白色像素叠加至另一图像?叠加后白边问题排查
问题描述
我有两张512x512的图像:
- 第一张是32位深度PNG(白色背景),需要提取其中非白色像素(云层)
- 第二张是24位深度JPG,作为像素叠加目标
我用C#实现了像素读取与叠加,但生成的BMP图像中叠加的像素出现白边;另外调整判断条件中的Alpha值(从255改为1或100)时,结果会变成和原PNG图像一致,只有Alpha为255时结果接近预期。
代码实现
核心逻辑类
using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Weather { public class RadarPixels { public RadarPixels(Bitmap image1, Bitmap image2) { ReadSetPixels(image1, image2); } private void ReadSetPixels(Bitmap image1 , Bitmap image2) { for(int x = 0; x < image1.Width; x++) { for(int y = 0; y < image1.Height; y++) { Color pixelColor = image1.GetPixel(x, y); if (pixelColor != Color.FromArgb(255,255,255,255)) { image2.SetPixel(x, y, pixelColor); } } } image2.Save(@"d:\mynewbmp.bmp"); image1.Dispose(); image2.Dispose(); } } }
调用代码
public Form1() { InitializeComponent(); RadarPixels rp = new RadarPixels( new Bitmap(Image.FromFile(@"D:\1.png")), new Bitmap(Image.FromFile(@"D:\2.jpg"))); }
问题原因分析
- 白边问题:32位PNG的云层边缘通常存在半透明白色过渡像素(即R=255、G=255、B=255但Alpha<255的像素),当前判断条件仅排除了完全不透明的纯白像素,这些半透明白像素会被保留并叠加到JPG上,最终显示为白边。
- Alpha值调整异常:当把判断条件中的Alpha改为1或100时,
Color.FromArgb(1,255,255,255)是几乎完全透明的白色,PNG中绝大多数非纯白像素都不等于这个颜色,导致所有非纯白像素(包括半透明白)都被叠加,最终结果和原PNG一致。
解决方法
1. 修正像素判断逻辑
直接排除所有纯白像素(不管Alpha值),只保留有效云层像素:
Color pixelColor = image1.GetPixel(x, y); // 排除所有RGB为纯白的像素 if (!(pixelColor.R == 255 && pixelColor.G == 255 && pixelColor.B == 255)) { image2.SetPixel(x, y, pixelColor); }
如果需要兼容接近纯白的过渡像素,可添加阈值判断:
int threshold = 240; // 可根据实际情况调整 if (!(pixelColor.R >= threshold && pixelColor.G >= threshold && pixelColor.B >= threshold)) { image2.SetPixel(x, y, pixelColor); }
2. 正确处理半透明像素叠加
目标JPG是24位无Alpha通道,直接SetPixel会丢弃Alpha值,导致半透明像素显示异常。手动计算混合色实现正确叠加:
Color sourcePixel = image1.GetPixel(x, y); Color targetPixel = image2.GetPixel(x, y); // 基于Alpha通道计算混合RGB值 int r = (sourcePixel.R * sourcePixel.A + targetPixel.R * (255 - sourcePixel.A)) / 255; int g = (sourcePixel.G * sourcePixel.A + targetPixel.G * (255 - sourcePixel.A)) / 255; int b = (sourcePixel.B * sourcePixel.A + targetPixel.B * (255 - sourcePixel.A)) / 255; Color blendedColor = Color.FromArgb(255, r, g, b); image2.SetPixel(x, y, blendedColor);
3. 性能优化(可选)
GetPixel和SetPixel性能较低,可使用LockBits直接操作图像内存提升效率:
private void ReadSetPixels(Bitmap image1, Bitmap image2) { Rectangle rect = new Rectangle(0, 0, image1.Width, image1.Height); var bmpData1 = image1.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly, image1.PixelFormat); var bmpData2 = image2.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, image2.PixelFormat); IntPtr ptr1 = bmpData1.Scan0; IntPtr ptr2 = bmpData2.Scan0; int bytes = Math.Abs(bmpData1.Stride) * image1.Height; byte[] rgbValues1 = new byte[bytes]; byte[] rgbValues2 = new byte[bytes]; System.Runtime.InteropServices.Marshal.Copy(ptr1, rgbValues1, 0, bytes); System.Runtime.InteropServices.Marshal.Copy(ptr2, rgbValues2, 0, bytes); // 遍历像素:32位PNG是BGRA格式,24位JPG是BGR格式 for (int y = 0; y < image1.Height; y++) { for (int x = 0; x < image1.Width; x++) { int index1 = y * bmpData1.Stride + x * 4; int b1 = rgbValues1[index1]; int g1 = rgbValues1[index1 + 1]; int r1 = rgbValues1[index1 + 2]; int a1 = rgbValues1[index1 + 3]; // 排除纯白像素 if (!(r1 == 255 && g1 == 255 && b1 == 255)) { int index2 = y * bmpData2.Stride + x * 3; // 计算混合色 int r = (r1 * a1 + rgbValues2[index2 + 2] * (255 - a1)) / 255; int g = (g1 * a1 + rgbValues2[index2 + 1] * (255 - a1)) / 255; int b = (b1 * a1 + rgbValues2[index2] * (255 - a1)) / 255; rgbValues2[index2] = (byte)b; rgbValues2[index2 + 1] = (byte)g; rgbValues2[index2 + 2] = (byte)r; } } } System.Runtime.InteropServices.Marshal.Copy(rgbValues2, 0, ptr2, bytes); image1.UnlockBits(bmpData1); image2.UnlockBits(bmpData2); image2.Save(@"d:\mynewbmp.bmp"); image1.Dispose(); image2.Dispose(); }
内容的提问来源于stack exchange,提问作者Daniel Lip
相关产品推荐
相关产品推荐

