1

Performing the same Laplacian operation in C++ and Python appears to produce different results. Notably, the C++ variant does not report negative slopes in the result matrix.

I am using (C++):

Laplacian(img.clone(), img, CV_16S, 1, 1, 0, BORDER_DEFAULT);

and (Python):

img = cv2.Laplacian(img, ddepth=cv2.CV_16S, ksize=1, scale=1, delta=0, borderType=cv2.BORDER_DEFAULT)

By processing the C++ data into a more friendly text format:

outfile.open("cpp.csv");
outfile.close();
for (size_t i = 0; i < img.size().width; i++) {
    outfile.open("cpp.csv", std::ios_base::app);
    for (size_t j = 0; j < img.size().height; j++) {
        outfile << (int16_t)img.at<uchar>(j, i);
        if (j < img.size().height - 1) {
            outfile << ",";
        }
    }
    outfile << std::endl;
    outfile.close();
}

I can then compare the values with Numpy.

cpp = np.loadtxt("./cpp.csv", delimiter=',')
print("python lap min:", np.min(img))
print("c++    lap min:", np.min(cpp))

This output shows us that the CPP code is not recording negative values:

python lap min: -133
c++    lap min: 0.0

So what is happening here? Surely I have made some sort of error, because there must be negative values in a Laplacian transformation somewhere if the image luminosity values are not static.

Image Source

The source image for each test case is a 1920x1080 full color PNG image with distinct light and dark areas. I'm using a photo of the moon. The image is converted to CV_8UC1 prior to the Laplace transformation.

Strict Typing

I have explicitly told C++ to store data with the int16_t type, which is implicitly signed by default, rather than just an int. This typing matches the output from the Laplacian storage matrix (CV_16S). Adding an explicit "signed" to "int16_t" produces the same result. My understanding is that the at function requires uchar typing similar to this SO answer. This is where I was wrong!!!

Version Note

Python OpenCV version: 4.1.1 (or 4.4.0, see edit section)

C++ OpenCV version: 3.4.1 (or 4.4.0, see edit section)

The documentation for these versions (3.4.1 and 4.1.1) shows no appreciable difference between the two.

Similar Questions

Laplacian variation between C++ and Python appears common on SO, but nothing had anything useful for this. I read:

Edit - Upgrading C++ OpenCV lib

I built OpenCV v.4.4 from git, and compiled the C++ version of the code with pkg-config --cflags --libs opencv4. The same error persists. (cv2.getBuildInformation() says the Python is running 4.4 as well).

Edit2 - context from a Git issue:

I found a similar issue on the github repo (here) where the solution was storing the Laplacian result as a new Mat. My original code uses Laplacian(img.clone(), img, ...) which should produce a "deep copy" of the img matrix in place, avoiding the problem reported by the OP. I tested my code with a unique matrix anyway Laplacian(outputimg, img, ...) and saw no change in results.

Edit3 - Generalizing the problem

I processed the original code, this time replacing the Laplacian filter with either a Sobel and a custom filter2D filter. The same result is observed, Python produces good values while C++ does not.

Edit4 - Post-answer cleanup

Since this was a simple typing error, I have crossed out most of the extraneous information. This leaves it my original attempts readable, but it makes it obvious that they were not useful in the end.

WesH
  • 460
  • 5
  • 15
  • Have you tried using the same version of OpenCV for both languages? It's possible there was a bugfix to the C++ interface at some point to fix not having negative values... – MattDMo Jun 09 '20 at 20:57
  • @MattDMo I updated to v4.4 and have the same problem. – WesH Jun 10 '20 at 18:52
  • 1
    Your C++ implementation is calling `Mat.at` but the data is not `uchar`, it is `int16_t`. I see you are casting it *afterwards* but it's already incorrectly interpreted at that point. You're basically only reading the first byte of the two byte value and interpreting it as an unsigned `uint8_t`, and then casting that value to an `int16_t` which won't change the value. So I believe you'll only see values in [0, 255], even though you should see values in [−32768, 32767] – alkasm Jun 10 '20 at 23:00
  • @alkasm The ``int16_t`` helped. I thought the ``uchar`` was the type of the values for my ``x`` and ``y`` coordinate. You should add this as the answer. You can also add it at: https://answers.opencv.org/question/231134/laplacian-output-in-c-excludes-negative-values-but-not-in-python/ for bonus points. – WesH Jun 10 '20 at 23:18
  • @WesH I just wasn't sure if it was your only issue/a complete answer as I wasn't able to validate it. However, I'm slightly leaning towards closing this question as a typo, since, even though the answer is potentially helpful, the question actually presumes a lot more is going on which is not relevant, and isn't likely to be found/useful for future readers. – alkasm Jun 11 '20 at 03:00
  • @alkasm I am going to edit the question to reflect the typo. Other folks encountering similar problems may not be aware that they need to check this typing. – WesH Jun 11 '20 at 19:15

1 Answers1

1

Your C++ implementation is calling Mat::at<uchar> which means "interpret the data at this location as uchar (i.e. uint8_t)" but your data is of course short (i.e. int16), not uchar. From the docs on Mat::at, note that they specify:

If matrix is of type CV_16S then use Mat.at<short>(y,x).

alkasm
  • 22,094
  • 5
  • 78
  • 94