sop32
sop32

Reputation: 15

Dilation Image on OpenCV C/C++

I want to create a dilation image using a kernel that runs through the entire image and checks if the kernel zone has 0, if so, it gives the new image a pixel of 255. My code is giving me an all black dst and I don't know why. This is the code:

Mat vcpi_binary_dilate(Mat src)
{
    if (src.empty())
    {
        cout << "Failed to load image.";
        return src;
    }

    Mat dst(src.rows, src.cols, CV_8UC1, Scalar(0));

    const int kernnel = 3;
    int array[kernnel * kernnel] = {};

    for (int y = kernnel / 2; y < src.cols - kernnel / 2; y++)
    {
        for (int x = kernnel / 2; x < src.rows - kernnel / 2; x++)
            for (int yk = -kernnel / 2; yk <= kernnel / 2; yk++)
            {
                for (int xk = -kernnel / 2; xk <= kernnel / 2; xk++)
                {
                    if (src.at<uchar>(y + yk, x + xk) == 0)
                    {
                        dst.at<uchar>(y + yk, x + xk) = 255;
                    }
                }
            }
    }

    imshow("Image ", src);
    imshow("Image dilate", dst);
    waitKey(0);

    return dst;
}

I hope to have an output image of this type.

Upvotes: 0

Views: 215

Answers (1)

wohlstad
wohlstad

Reputation: 28074

I am not sure about the algorithm you are trying to implement.

But there is one thing that is definately wrong:
The image dimensions are mixed up-
cols is the width and corresponds to the x axis.
rows is the height and corresponds to the y axis.
This is causing you to access the images using cv::Mat::at with invalid coordinates.

Therefore you need to change:

for (int y = kernnel / 2; y < src.cols - kernnel / 2; y++)
{
    for (int x = kernnel / 2; x < src.rows - kernnel / 2; x++)
    {

To:

//--------------------------------vvvv--------------------
for (int y = kernnel / 2; y < src.rows - kernnel / 2; y++)
{
//------------------------------------vvvv--------------------
    for (int x = kernnel / 2; x < src.cols - kernnel / 2; x++)
    {

Note that this is consistent with your calls ...at<uchar>(y + yk, x + xk), since cv::Mat::at expects the row (i.e. y coordinate) first.

A side note: Why is "using namespace std;" considered bad practice?.


Edit:

After having a look at the matlab code in your comment, which applies an algorithm different than what you described in your question, you'll need to do the following changes:

  1. Call something equivalent to matlab's im2bw.
  2. Update dst current pixel, not the one in the neighborhood.

Maybe something like:

cv::Mat vcpi_binary_dilate(cv::Mat src) {

    if (src.empty()) {
        std::cout << "Failed to load image.";
        return src;
    }

    cv::threshold(src, src, 127, 255, CV_THRESH_BINARY);

    cv::Mat dst(src.rows, src.cols, CV_8UC1, cv::Scalar(255));  // <-- Replacement for im2bw, might need tuning.

    const int kernnel = 3;
    int array[kernnel * kernnel] = {};

    for (int y = kernnel / 2; y < src.rows - kernnel / 2; y++)
    {
        for (int x = kernnel / 2; x < src.cols - kernnel / 2; x++)
        {
            for (int yk = -kernnel / 2; yk <= kernnel / 2; yk++)
            {
                for (int xk = -kernnel / 2; xk <= kernnel / 2; xk++)
                {
                    if (src.at<uchar>(y + yk, x + xk) == 0) {
                        dst.at<uchar>(y, x) = 0;   // <-- Update the current pixel, not the one in the neighborhood.
                    }
                }
            }
        }
    }

    cv::imshow("Image ", src);
    cv::imshow("Image dilate", dst);
    cv::waitKey(0);
    return dst;
}

Upvotes: 1

Related Questions