winforms 当比较两个图像如何创建一个新的图像与不同的像素和颜色?

zmeyuzjn  于 2022-11-17  发布在  其他
关注(0)|答案(1)|浏览(167)

我有三个图像。
第一个图像是参考图像,其中没有任何云。

第二个图像是我想比较的两个图像之一,其中有云。

第三张图像中也有云,这是要比较的第二张图像。

我想做的是把两个图像中不同的像素与云放在空的图像上。
这是我现在使用的代码:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;

namespace ImageComparison
{
    public partial class Form1 : Form
    {
        string fname1, fname2;

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            progressBar1.Visible = false;
            pictureBox1.Visible = false;
        }

        private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            openFileDialog1.FileName = "";
            openFileDialog1.Title = "Images";
            openFileDialog1.Filter = "All Images|*.jpg; *.bmp; *.png";
            openFileDialog1.ShowDialog();
            if (openFileDialog1.FileName.ToString() != "")
            {
                fname1 = openFileDialog1.FileName.ToString();
            }
        }

        private void linkLabel2_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            openFileDialog2.FileName = "";
            openFileDialog2.Title = "Images";
            openFileDialog2.Filter = "All Images|*.jpg; *.bmp; *.png";
            openFileDialog2.ShowDialog();
            if (openFileDialog2.FileName.ToString() != "")
            {
                fname2 = openFileDialog2.FileName.ToString();
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Bitmap myBitmap = new Bitmap(Image.FromFile(@"d:\Comparison\radar_without_clouds.jpg"));

            progressBar1.Visible = true;
            int count1 = 0,count2 = 0;
            bool flag = true;
            string img1_ref, img2_ref;
            Bitmap img1 = new Bitmap(fname1);
            Bitmap img2 = new Bitmap(fname2);
            progressBar1.Maximum = img1.Width;
            if (img1.Width == img2.Width && img1.Height == img2.Height)
            {
                for (int i = 0; i < img1.Width; i++)
                {
                    for (int j = 0; j < img1.Height; j++)
                    {
                        img1_ref = img1.GetPixel(i, j).ToString();
                        img2_ref = img2.GetPixel(i, j).ToString();
                        if (img1_ref != img2_ref)
                        {
                            Color p = img1.GetPixel(i, j);
                            myBitmap.SetPixel(i, j, p);

                            count2++;
                            flag = false;
                            break;
                        }
                        count1++;
                    }
                    progressBar1.Value++;
                }

                myBitmap.Save(@"d:\Comparison\result.bmp");
                myBitmap.Dispose();

                if (flag == false)
                    MessageBox.Show("Sorry, Images are not same , " + count2 + " wrong pixels found");
                else
                    MessageBox.Show(" Images are same , " + count1 + " same pixels found and " + count2 + " wrong pixels found");
            }
            else
                MessageBox.Show("can not compare this images");
            this.Dispose();
        }
    }
}

result.bmp图像中的结果为:

现在,结果图像是空白图像,两个图像的像素不同,但如果我看两个图像,我比较有更多的不同像素。
用红圈标记两个图像中的不同区域:例如,两个图像中的右上角区域是不同的,但我在result.bmp图像中看不到它。


指令集
我想在result.bmp图像中看到两个比较图像中的所有云,就像是另一个图像中不存在的云的混合。
或者也许我错过了一些关于比较逻辑的东西??用我的眼睛看,我看到两个图像中有更多不同的像素,然后在result.bmp中有什么
也许我没有设置所有不同的像素在result.bmp?也许这行是错误的或不够?

myBitmap.SetPixel(i, j, p);
jv2fixgn

jv2fixgn1#

我没有看你的图像比较逻辑,但我认为问题是由参考图像和其他图像中未被云覆盖的像素彼此不同这一事实引起的。在这种情况下,你不能简单地比较像素。相反,你应该使用一些数学来判断像素是否不同。你需要的是认为R,G,B通道作为坐标轴,像素作为该坐标系中的点,并计算两个点的距离,即像素。如果距离小于某个容差值,则将像素视为相同,否则像素为不同。
我创建了一个简单的ColorBgr结构来更有效地处理像素。

public struct ColorBgr : IEquatable<ColorBgr>
{
    public byte B;
    public byte G;
    public byte R;
    public ColorBgr(byte b, byte g, byte r)
    {
        B = b;
        G = g;
        R = r;
    } 
    public double GetDistanceSquared(ColorBgr other)
    {
        double db = other.B - B;
        double dg = other.G - G;
        double dr = other.R - R;
        return db * db + dg * dg + dr * dr;
    }
    public static bool operator ==(ColorBgr left, ColorBgr right)
    {
        return left.Equals(right);
    }
    public static bool operator !=(ColorBgr left, ColorBgr right)
    {
        return !(left == right);
    }
    public bool Equals(ColorBgr other)
    {
        return this.B == other.B && this.G == other.G && this.R == other.R;
    }

    public override bool Equals([NotNullWhen(true)] object? obj)
    {
        if(obj is ColorBgr) 
            return Equals((ColorBgr)obj); 
        return false;
    }

    public override int GetHashCode()
    {
        return new byte[] { B, G, R }.GetHashCode();
    } 
}

创建一个方法,该方法采用参考图像、其他图像、结果图像、容差值,并将不同的像素复制到结果图像中。

private unsafe void CompositeDifferences(Bitmap referenceImage, Bitmap otherImage, Bitmap resultImage,  double tolerance)
{
    if (referenceImage.Width != otherImage.Width && referenceImage.Height != otherImage.Height)
        return; 
    int width = referenceImage.Width;
    int height = referenceImage.Height;

    BitmapData referenceImageData = referenceImage.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, referenceImage.PixelFormat);
    BitmapData otherImageData = otherImage.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, otherImage.PixelFormat);
    BitmapData resultImageData = resultImage.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, resultImage.PixelFormat);

    int referenceStride = referenceImageData.Stride;
    int otherStride = otherImageData.Stride;
    int resultStride = resultImageData.Stride;

    byte* referenceScan0 = (byte*)referenceImageData.Scan0;
    byte* otherScan0 = (byte*)otherImageData.Scan0;
    byte* resultScan0 = (byte*)resultImageData.Scan0;

    int referenceBytesPerPixel = Image.GetPixelFormatSize(referenceImage.PixelFormat) / 8;
    int otherBytesPerPixel = Image.GetPixelFormatSize(otherImage.PixelFormat) / 8;
    int resultBytesPerPixel = Image.GetPixelFormatSize(resultImage.PixelFormat) / 8;

    for (int y = 0; y < height; y++)
    {
        byte* referenceCurrentRow = referenceScan0 + y * referenceStride;
        byte* otherCurrentRow = otherScan0 + y * otherStride;
        byte* resultCurrentRow = resultScan0 + y * resultStride;
        for (int x = 0; x < width; x++)
        {
            ColorBgr* referencePixel = (ColorBgr*)(referenceCurrentRow + x * referenceBytesPerPixel);
            ColorBgr* otherPixel = (ColorBgr*)(otherCurrentRow + x * otherBytesPerPixel);
            ColorBgr* resultPixel = (ColorBgr*)(resultCurrentRow + x * resultBytesPerPixel);
            if (referencePixel->GetDistanceSquared(*otherPixel) >= tolerance * tolerance)
                *resultPixel = *otherPixel; 
        }
    }

    referenceImage.UnlockBits(referenceImageData);
    otherImage.UnlockBits(otherImageData);
    resultImage.UnlockBits(resultImageData);
}

此外,如果不想更改雷达圆圈外部的像素,可以再添加一个名为radarRadius的参数,以检查是否位于圆圈内部。

private unsafe void CompositeDifferences(Bitmap referenceImage, Bitmap otherImage, Bitmap resultImage, int radarRadius, double tolerance)
{
    if (referenceImage.Width != otherImage.Width && referenceImage.Height != otherImage.Height)
        return; 
    int width = referenceImage.Width;
    int height = referenceImage.Height;

    BitmapData referenceImageData = referenceImage.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, referenceImage.PixelFormat);
    BitmapData otherImageData = otherImage.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, otherImage.PixelFormat);
    BitmapData resultImageData = resultImage.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, resultImage.PixelFormat);

    int referenceStride = referenceImageData.Stride;
    int otherStride = otherImageData.Stride;
    int resultStride = resultImageData.Stride;

    byte* referenceScan0 = (byte*)referenceImageData.Scan0;
    byte* otherScan0 = (byte*)otherImageData.Scan0;
    byte* resultScan0 = (byte*)resultImageData.Scan0;

    int referenceBytesPerPixel = Image.GetPixelFormatSize(referenceImage.PixelFormat) / 8;
    int otherBytesPerPixel = Image.GetPixelFormatSize(otherImage.PixelFormat) / 8;
    int resultBytesPerPixel = Image.GetPixelFormatSize(resultImage.PixelFormat) / 8;

    for (int y = 0; y < height; y++)
    {
        byte* referenceCurrentRow = referenceScan0 + y * referenceStride;
        byte* otherCurrentRow = otherScan0 + y * otherStride;
        byte* resultCurrentRow = resultScan0 + y * resultStride;
        for (int x = 0; x < width; x++)
        {
            if (!IsInsideCircle(x, y, width / 2, height / 2, radarRadius))
                continue;
            ColorBgr* referencePixel = (ColorBgr*)(referenceCurrentRow + x * referenceBytesPerPixel);
            ColorBgr* otherPixel = (ColorBgr*)(otherCurrentRow + x * otherBytesPerPixel);
            ColorBgr* resultPixel = (ColorBgr*)(resultCurrentRow + x * resultBytesPerPixel);
            if (referencePixel->GetDistanceSquared(*otherPixel) >= tolerance * tolerance)
                *resultPixel = *otherPixel; 
        }
    }

    referenceImage.UnlockBits(referenceImageData);
    otherImage.UnlockBits(otherImageData);
    resultImage.UnlockBits(resultImageData);
}
private bool IsInsideCircle(Point p, Point center, int radius)
{
    int dx = p.X - center.X;
    int dy = p.Y - center.Y;

    return (dx * dx + dy * dy) <= (radius * radius);
}
private bool IsInsideCircle(int x, int y, int centerX, int centerY, int radius)
{
    return IsInsideCircle(new Point(x, y), new Point(centerX, centerY), radius);
}

下面是一个完整的Winforms示例。

using System.Drawing.Imaging;

namespace CompositeDifferences
{
    public partial class MainWindow : Form
    {
        public MainWindow()
        {
            InitializeComponent();
            
        }
        private Bitmap? m_referenceImage = null;
        private Bitmap? m_seocondImage = null;
        private Bitmap? m_thirdImage = null;
        private Bitmap? m_resultImage = null;
        private int m_tolerance = 120;
        private int m_radarRadius = 256;
        protected override void OnHandleCreated(EventArgs e)
        {
            m_referenceImage = (Bitmap)Image.FromFile("referenceImage.jpg");
            m_seocondImage = (Bitmap)Image.FromFile("secondImage.png");
            m_thirdImage = (Bitmap)Image.FromFile("thirdImage.png");
            m_resultImage = (Bitmap)m_referenceImage.Clone();
        }
        protected override void OnShown(EventArgs e)
        {
            ProcessChanges();
        }
        private unsafe void CompositeDifferences(Bitmap referenceImage, Bitmap otherImage, Bitmap resultImage, int radarRadius, double tolerance)
        {
            if (referenceImage.Width != otherImage.Width && referenceImage.Height != otherImage.Height)
                return; 
            int width = referenceImage.Width;
            int height = referenceImage.Height;

            BitmapData referenceImageData = referenceImage.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, referenceImage.PixelFormat);
            BitmapData otherImageData = otherImage.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, otherImage.PixelFormat);
            BitmapData resultImageData = resultImage.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, resultImage.PixelFormat);

            int referenceStride = referenceImageData.Stride;
            int otherStride = otherImageData.Stride;
            int resultStride = resultImageData.Stride;

            byte* referenceScan0 = (byte*)referenceImageData.Scan0;
            byte* otherScan0 = (byte*)otherImageData.Scan0;
            byte* resultScan0 = (byte*)resultImageData.Scan0;

            int referenceBytesPerPixel = Image.GetPixelFormatSize(referenceImage.PixelFormat) / 8;
            int otherBytesPerPixel = Image.GetPixelFormatSize(otherImage.PixelFormat) / 8;
            int resultBytesPerPixel = Image.GetPixelFormatSize(resultImage.PixelFormat) / 8;

            for (int y = 0; y < height; y++)
            {
                byte* referenceCurrentRow = referenceScan0 + y * referenceStride;
                byte* otherCurrentRow = otherScan0 + y * otherStride;
                byte* resultCurrentRow = resultScan0 + y * resultStride;
                for (int x = 0; x < width; x++)
                {
                    if (!IsInsideCircle(x, y, width / 2, height / 2, radarRadius))
                        continue;
                    ColorBgr* referencePixel = (ColorBgr*)(referenceCurrentRow + x * referenceBytesPerPixel);
                    ColorBgr* otherPixel = (ColorBgr*)(otherCurrentRow + x * otherBytesPerPixel);
                    ColorBgr* resultPixel = (ColorBgr*)(resultCurrentRow + x * resultBytesPerPixel);
                    if (referencePixel->GetDistanceSquared(*otherPixel) >= tolerance * tolerance)
                        *resultPixel = *otherPixel; 
                }
            }

            referenceImage.UnlockBits(referenceImageData);
            otherImage.UnlockBits(otherImageData);
            resultImage.UnlockBits(resultImageData);
        }
        private bool IsInsideCircle(Point p, Point center, int radius)
        {
            int dx = p.X - center.X;
            int dy = p.Y - center.Y;

            return (dx * dx + dy * dy) <= (radius * radius);
        }
        private bool IsInsideCircle(int x, int y, int centerX, int centerY, int radius)
        {
            return IsInsideCircle(new Point(x, y), new Point(centerX, centerY), radius);
        }
        private void ProcessChanges()
        {
            CompositeDifferences(m_referenceImage, m_seocondImage, m_resultImage, m_radarRadius, m_tolerance);
            CompositeDifferences(m_referenceImage, m_thirdImage, m_resultImage,   m_radarRadius, m_tolerance);
            pictureBox1.Image = m_resultImage;
        } 
        private void trackBar1_ValueChanged(object sender, EventArgs e)
        {
            m_tolerance = trackBar1.Value;
            ProcessChanges();
        }
        private void trackBar2_ValueChanged(object sender, EventArgs e)
        {
            m_radarRadius = trackBar2.Value;
            ProcessChanges();
        }
        private struct ColorBgr : IEquatable<ColorBgr>
        {
            public byte B;
            public byte G;
            public byte R;
            public ColorBgr(byte b, byte g, byte r)
            {
                B = b;
                G = g;
                R = r;
            } 
            public double GetDistanceSquared(ColorBgr other)
            {
                double db = other.B - B;
                double dg = other.G - G;
                double dr = other.R - R;
                return db * db + dg * dg + dr * dr;
            }
            public static bool operator ==(ColorBgr left, ColorBgr right)
            {
                return left.Equals(right);
            }
            public static bool operator !=(ColorBgr left, ColorBgr right)
            {
                return !(left == right);
            }
            public bool Equals(ColorBgr other)
            {
                return this.B == other.B && this.G == other.G && this.R == other.R;
            }

            public override bool Equals(object? obj)
            {
                if(obj is ColorBgr) 
                    return Equals((ColorBgr)obj); 
                return false;
            }

            public override int GetHashCode()
            {
                return new byte[] { B, G, R }.GetHashCode();
            } 
        }
    } 
}

输出如下所示。

相关问题