Python OpenCV erode and dilate works differently as in C#

427 Views Asked by At

I am trying to convert some code for generating fiducial markers from C# to Python, and I am stuck at erode and dilate. OpenCV's implementation works differently than the C# implementation.

Here is the full C# code if anyone wants to reproduce the problem: https://pastecode.io/s/2NbQPPOLCy

Here are the relevant parts of C# code for the marker generator. It has its own erode and dilate functions.

This is how dilate and erode functions are called:

//erode, dilate
int rad = 12;
for (int j = 0; j < 5; j++)
{
  dilateBitmap(ref img, 5, rad);
  erodeBitmap(ref img, 5, rad);
}

Here is the implementation of C#'s dilate and erode functions:

public void erodeBitmap(ref Bitmap b, int thres, int radius)
{
    Rectangle rect = new Rectangle(0, 0, fileSize, fileSize);
    BitmapData imgData = b.LockBits(rect, ImageLockMode.WriteOnly, b.PixelFormat);


    Bitmap refImage = (Bitmap)b.Clone();
    BitmapData refData = refImage.LockBits(rect, ImageLockMode.ReadOnly, refImage.PixelFormat);

    List<Point> list = generateBallMask(radius);

    int border = (int)outerCircleTopLeft;

    for (int j = border; j < fileSize - border; j++)
    {
        for (int k = border; k < fileSize - border; k++)
        {
            if (!isWhite(refData, j, k))
            {
                int tot = countNonPixels(refData, j, k, true, list);
              

                if (tot > list.Count / 2)
                {
                    *((byte*)(imgData.Scan0 + k * imgData.Stride + j * 4)) = 255;
                    *((byte*)(imgData.Scan0 + k * imgData.Stride + j * 4) + 1) = 255;
                    *((byte*)(imgData.Scan0 + k * imgData.Stride + j * 4) + 2) = 255;
                    *((byte*)(imgData.Scan0 + k * imgData.Stride + j * 4) + 3) = 255;
                }
            }
        }
    }
    refImage.UnlockBits(refData);
    refImage.Dispose();
    b.UnlockBits(imgData);
}

public void dilateBitmap(ref Bitmap b, int thres, int radius)
{
    Rectangle rect = new Rectangle(0, 0, fileSize, fileSize);
    BitmapData imgData = b.LockBits(rect, ImageLockMode.WriteOnly, b.PixelFormat);


    Bitmap refImage = (Bitmap)b.Clone();
    BitmapData refData = refImage.LockBits(rect, ImageLockMode.ReadOnly, refImage.PixelFormat);

    List<Point> list = generateBallMask(radius);

    int border = (int)outerCircleTopLeft;

    for (int j = border; j < fileSize - border; j++)
    {
        for (int k = border; k < fileSize - border; k++)
        {
            if (!isBlack(refData, j, k))
            {
                int tot = countNonPixels(refData, j, k, false, list);

                if (tot > list.Count / 2)
                {
                    *((byte*)(imgData.Scan0 + k * imgData.Stride + j * 4)) = 0;
                    *((byte*)(imgData.Scan0 + k * imgData.Stride + j * 4) + 1) = 0;
                    *((byte*)(imgData.Scan0 + k * imgData.Stride + j * 4) + 2) = 0;
                    *((byte*)(imgData.Scan0 + k * imgData.Stride + j * 4) + 3) = 255;
                }
            }
        }
    }
    refImage.UnlockBits(refData);
    refImage.Dispose();
    b.UnlockBits(imgData);
}

This is how the kernel is generated in C#:

 public List<Point> generateBallMask(int r)
        {
            List<Point> maskList = new List<Point>();

            for (int i = -r; i <= r; i++)
            {
                for (int j = -r; j <= r; j++)
                {
                    if ((i == 0) && (j == 0))
                        continue;
                    if (i * i + j * j <= r * r)
                        maskList.Add(new Point(i, j));
                }
            }

                return maskList;
        }

And here is Python's minimal code:

import numpy as np
import cv2


img = cv2.imread("/home/matjaz/UR/stag/ref/marker generator/HD23/preErodeAndDilate.png", cv2.IMREAD_COLOR)

kernelRadius = 12
kernel = np.zeros((2 * kernelRadius, 2 * kernelRadius), np.uint8) 
for i in range(0, 2 * kernelRadius):
    for j in range(0, 2 * kernelRadius):
        
        if i == kernelRadius and j == kernelRadius:
            continue
        
        if (i - kernelRadius) ** 2 + (j - kernelRadius) ** 2 <= kernelRadius ** 2:
            kernel[i][j] = 255


for _ in range(5):
    img = cv2.dilate(img, kernel, iterations=1)
    img = cv2.erode(img, kernel, iterations=1)


cv2.imwrite("/home/matjaz/UR/stag/ref/marker generator/HD23/afterErodeAndDilatePython.png", img)

This is the image that is generated before dilation and erosion. I managed to get the same image before erosion and dilation in both C# and Python. Pre erode and dilate

And this is the image after erode and dilate in C#: enter image description here

This is the image after erode and dilate step in Python: enter image description here

I would like to know how to achieve in Python the same image as after erode and dilate in C#

I have also tried to convert the C#'s code for dilation and erosion into python, but with without success, because the C# implementation uses pointers, which are not availabel in Python.

I got the C# code from this repo: https://github.com/bbenligiray/stag and modified it so it is all in one file

0

There are 0 best solutions below