winforms 如何读取像素并将其设置在另一个图像上?设置后的像素有白色边框

ao218c7q  于 2023-01-09  发布在  其他
关注(0)|答案(2)|浏览(156)

我有两个图像,第一个是白色背景,大小是512x512类型PNG位深度32:从这个图像中我想读取不是白色(云)的像素,并将它们放在另一个图像上。

第二个图像是我想把像素放在上面的图像:该图像尺寸也是512 × 512,但是类型为jpg,且Bith深度为24。

这个类是我用来阅读和设置像素的:

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();
        }
    }
}

以表格1的形式使用:

public Form1()
        {
            InitializeComponent();

            RadarPixels rp = new RadarPixels(
                new Bitmap(Image.FromFile(@"D:\1.png")),
                new Bitmap(Image.FromFile(@"D:\2.jpg")));
        }

其中1.png是具有白色背景的图像。
图像mynewbmp.bmp的结果是

不知道为什么它有一些白色边框周围的设置像素,如果它复制了所有的像素颜色的权利。
如果我把Color.FromArgb(255,255,255,255)的alpha值从255改为1或100,它会使mynewbmp.bmp和1.png一样,只有当alpha值为255时,它才更接近我想要的。

brccelvz

brccelvz1#

正如其他人所说,有些像素不是纯白的,所以它们包含在最终的图像中。jtxkopt设置容差的解决方案很棒,但根据输入图像的质量和边缘的平滑度,容差可能需要不断调整以获得良好的效果。
最后,您的解决方案和Jtxkopt的解决方案都模拟了一个二进制掩码。这意味着您基于一个掩码图像将两个图像合成在一起,而该掩码图像只有2个值,黑色和白色。如果一个像素是黑色绘制图像1,如果一个像素是白色,则绘制image2。只有在您的情况下,您的2个值是白色和非白色。这是理论上的二进制掩码图像,只有黑色和白色像素:

二进制蒙版有非常粗糙的边缘,有时被称为"裁剪"蒙版,因为它就像用sizzors裁剪东西。创建二进制蒙版时,主要问题是试图确定哪些像素变白,哪些像素变黑,问题几乎总是在事物的边缘,jtxkopt的公差解决方案只是帮助做出决定并画出最终的界限。用户Andy建议使用MakeTransparent,但其结果与二进制遮罩类似,因为您为其指定1种颜色来决定哪些应该透明,哪些不应该透明(打开或关闭)。
合成两张图像的另一种方法是使用灰度蒙版。您可以使用许多灰色阴影来描述如何将两张图像混合在一起,而不是使用黑色和白色。如果像素是纯白和纯黑,则可以获得二元蒙版的所有优点,但如果像素是灰色,则可以根据其灰度值将两张图像混合在一起。这会创建更平滑的合成。下面是灰度蒙版:

下面是一个简单的解决方案,它获取每个像素,转换为一个简单的灰色像素,并使用该灰色像素设置image1的Alpha通道(透明度),以便可以将其合成到image2的顶部。

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);
                    // just average R, G, and B values to get gray. Then invert by 255.
                    int invertedGrayValue = 255 - (int)((pixelColor.R + pixelColor.G + pixelColor.B) / 3);
                    // this keeps the original pixel color but sets the alpha value
                    image1.SetPixel(x, y, Color.FromArgb(invertedGrayValue, pixelColor));
                }
            }
            // composite image1 on top of image2
            using (Graphics g = Graphics.FromImage(image2))
            {
                g.CompositingMode = CompositingMode.SourceOver;
                g.CompositingQuality = CompositingQuality.HighQuality;
                g.DrawImage(image1, new Point(0, 0));         
            }

            image2.Save(@"d:\mynewbmp.bmp");
            image1.Dispose();
            image2.Dispose();
        }
    }
}

结果是:

这种方法有优点也有缺点,优点是不必调整容差值,而且生成的图像不只是图像1中的像素和图像2中的一些像素,像素是从两幅图像中混合出来的。2缺点是云有点透明,不那么明显。3这可以通过调整灰度掩模的对比度来解决,这样亮的区域更亮,暗的区域更暗。或者,您可以将image1多次合成到image2上以获得"累加"效果。或者,您可以添加另一个变量以减少或增加最终alpha值。
我希望这个替代解决方案是有用的。

  • 更新以澄清-
    更清楚地说,在这个解决方案中,我们在使用alpha通道的值之前反转灰色像素。这是因为alpha通道将0视为不可见,将255视为可见。因此,我们希望通过从255中减去像素,将白色255反转为黑色0。最终结果是反转的灰度遮罩,如下所示:

此时,越接近白色的东西在合成图中越明显。由于此灰度图像中的云有点偏深灰色,它们将与背景图像混合并失去一点活力。我们希望调整此图像,使黑色保持黑色,但灰色变得更亮以恢复活力。
通过一个简单的改变,我们可以忽略所有黑色的0值,因为我们希望它们保持不可见,但增加所有其他值的alpha。

if(invertedGrayValue > 0){ invertedGrayValue = 255; }

这就像一个二进制蒙版。所有黑色值将保持不可见,所有非黑色值完全可见。但这违背了使用灰度蒙版的目的。但我们可以设置一个"容差"值如下:

if(invertedGrayValue > tolerance){ invertedGrayValue = 255; }

这将结合灰度解决方案和"容差"解决方案。例如,如果我们将容差设置为64,这意味着任何大于64的灰度值将100%可见,而任何低于64的灰度值将不可见或略微可见。这意味着白色边缘(这是原来的问题)不会完全去除,只会与背景柔和地结合。所以这个解决方案只是"软化边缘"。
以下是公差为64时的结果:

最终代码为:

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)
        {
            int tolerance = 64;
            for(int x = 0; x < image1.Width; x++)
            {
                for(int y = 0; y < image1.Height; y++)
                {
                    Color pixelColor = image1.GetPixel(x, y);
                    // just average R, G, and B values to get gray. Then invert by 255.
                    int invertedGrayValue = 255 - (int)((pixelColor.R + pixelColor.G + pixelColor.B) / 3);
                    if(invertedGrayValue > tolerance){ invertedGrayValue = 255; }
                    // this keeps the original pixel color but sets the alpha value
                    image1.SetPixel(x, y, Color.FromArgb(invertedGrayValue, pixelColor));
                }
            }
            // composite image1 on top of image2
            using (Graphics g = Graphics.FromImage(image2))
            {
                g.CompositingMode = CompositingMode.SourceOver;
                g.CompositingQuality = CompositingQuality.HighQuality;
                g.DrawImage(image1, new Point(0, 0));         
            }

            image2.Save(@"d:\mynewbmp.bmp");
            image1.Dispose();
            image2.Dispose();
        }
    }
}
tyky79it

tyky79it2#

如注解中所述,白色像素并不完全是白色的。因此,您应该计算到白色的距离,并检查计算出的距离是否大于某个容差值。
要获得所需结果,应调整以下公差值。

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)
        {
            double tolerance = 46; // Don't forget adjust it.
            for(int x = 0; x < image1.Width; x++)
            {
                for(int y = 0; y < image1.Height; y++)
                {
                    Color pixelColor = image1.GetPixel(x, y);
                    if (GetDistance(pixelColor, Color.White) > tolerance)
                    {
                        image2.SetPixel(x, y, pixelColor);
                    }
                }
            }

            image2.Save(@"d:\mynewbmp.bmp");

            image1.Dispose();
            image2.Dispose();
        }
    }
    private double GetDistance(Color color1, Color color2)
    {
        double dr = color1.R - color2.R;
        double dg = color1.G - color2.G;
        double db = color1.B - color2.B;
        
        return Math.Sqrt(dr * dr + dg * dg + db * db);  
    }
}

相关问题