Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Find Subimage in larger image in C#

I want to program a function that gets 2 Bitmaps (small image and large image). It should search the position of the small one in the large image. Then it should return the (X/Y) Point where the subimage is located at.

I have already found a bunch of solutions, but all were written in Java, for example this one: Find known sub image in larger image So I tried to reprogram them in C#, but I always failed, because the code is not real Java. The method Image.getPixel(x, y) does not exist there and I don't really know, what the threshold should be.

like image 264
lui1000 Avatar asked Jul 05 '12 11:07

lui1000


1 Answers

There you go. This function return all locations found in the larg image, because small image could also multiple times contained in the large one. If you want, you can rewrite the function unsafe for more performance.

public static List<Point> GetSubPositions(Bitmap main, Bitmap sub) {
    List<Point> possiblepos = new List<Point>();

    int mainwidth = main.Width;
    int mainheight = main.Height;

    int subwidth = sub.Width;
    int subheight = sub.Height;

    int movewidth = mainwidth - subwidth;
    int moveheight = mainheight - subheight;

    BitmapData bmMainData = main.LockBits(new Rectangle(0, 0, mainwidth, mainheight), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
    BitmapData bmSubData = sub.LockBits(new Rectangle(0, 0, subwidth, subheight), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);

    int bytesMain = Math.Abs(bmMainData.Stride) * mainheight;
    int strideMain = bmMainData.Stride;
    System.IntPtr Scan0Main = bmMainData.Scan0;
    byte[] dataMain = new byte[bytesMain];
    System.Runtime.InteropServices.Marshal.Copy(Scan0Main, dataMain, 0, bytesMain);

    int bytesSub = Math.Abs(bmSubData.Stride) * subheight;
    int strideSub = bmSubData.Stride;
    System.IntPtr Scan0Sub = bmSubData.Scan0;
    byte[] dataSub = new byte[bytesSub];
    System.Runtime.InteropServices.Marshal.Copy(Scan0Sub, dataSub, 0, bytesSub);

    for (int y = 0; y < moveheight; ++y) {
        for (int x = 0; x < movewidth; ++x) {
            MyColor curcolor = GetColor(x, y, strideMain, dataMain);

            foreach (var item in possiblepos.ToArray()) {
                int xsub = x - item.X;
                int ysub = y - item.Y;
                if (xsub >= subwidth || ysub >= subheight || xsub < 0)
                    continue;

                MyColor subcolor = GetColor(xsub, ysub, strideSub, dataSub);

                if (!curcolor.Equals(subcolor)) {
                    possiblepos.Remove(item);
                }
            }

            if (curcolor.Equals(GetColor(0, 0, strideSub, dataSub)))
                possiblepos.Add(new Point(x, y));
        }
    }

    System.Runtime.InteropServices.Marshal.Copy(dataSub, 0, Scan0Sub, bytesSub);
    sub.UnlockBits(bmSubData);

    System.Runtime.InteropServices.Marshal.Copy(dataMain, 0, Scan0Main, bytesMain);
    main.UnlockBits(bmMainData);

    return possiblepos;
}

private static MyColor GetColor(Point point, int stride, byte[] data) {
    return GetColor(point.X, point.Y, stride, data);
}

private static MyColor GetColor(int x, int y, int stride, byte[] data) {
    int pos = y * stride + x * 4;
    byte a = data[pos + 3];
    byte r = data[pos + 2];
    byte g = data[pos + 1];
    byte b = data[pos + 0];
    return MyColor.FromARGB(a, r, g, b);
}

struct MyColor {
    byte A;
    byte R;
    byte G;
    byte B;

    public static MyColor FromARGB(byte a, byte r, byte g, byte b) {
        MyColor mc = new MyColor();
        mc.A = a;
        mc.R = r;
        mc.G = g;
        mc.B = b;
        return mc;
    }

    public override bool Equals(object obj) {
        if (!(obj is MyColor))
            return false;
        MyColor color = (MyColor)obj;
        if(color.A == this.A && color.R == this.R && color.G == this.G && color.B == this.B)
            return true;
        return false;
    }
}
like image 76
Wowa Avatar answered Nov 05 '22 13:11

Wowa