Home > Software engineering >  When comparing two images how to create a new image with the different pixels and colors?
When comparing two images how to create a new image with the different pixels and colors?

Time:10-03

I have 3 images.

The first image is a reference image empty one without any clouds in it.

empty image without clouds

The second image is one of the two images i want to compare and have clouds in it.

image with clouds

The third image also have clouds in it and this is the second image to compare.

image with clouds

What i want to do is to put the different pixels from both images with clouds on the empty image.

This is the code i'm using now :

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

The result in the result.bmp image is :

result

now the result image is the empty image with the different pixels from both images ? but if i look at the two images i'm comparing there are much more different pixels.

i marked with red circle the different areas in both images : For example the top right area in both images is different but i din't see it in the result.bmp image.

marked differents

i want in the result.bmp image to see all the clouds from both compared images like a mix of the clouds that are not exist in the other image.

or maybe i'm missing something about the comparison logic ? with just looking with my eyes i see much more different pixels in both images then what there are in the result.bmp

maybe i'm not setting all the different pixels in to the result.bmp ? maybe this line is wrong or not enough ?

myBitmap.SetPixel(i, j, p);

CodePudding user response:

I didn't look at your image comparison logic but I think the problem is causing from the fact that the pixels not covered by the clouds in both reference image and other images are different form each other. That being case, you cannot simply compare the pixels. Instead, you should use some math to make decision that the pixels are different. What you need is to think the R, G, B channels as the coordinate axis and the pixels as the points in that coordinate system, and calculate the distance two points, that is, pixels. If the distance is less than some tolerance value, treat the pixels as if they are the same, and if not, then the pixels are different.

I have created a simple ColorBgr structure to process the pixels more efficiently.

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

Create a method that takes the reference image, other image, the result image, a tolerance value and copies the different pixels into the result image.

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

Also, if you want to not change the pixels outside of the radar circle, you can add one more parameter named radarRadius to check if you are inside of thre circle.

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

Here is a complete Winforms example.

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

The output is like below.

enter image description here

  • Related