bugGNU Octave - Bugs: bug #46547, image package: canny detector in...


bug #46547: image package: canny detector in edge() should use LoG as first step

Submitter:  Hartmut <hardy>
Submitted:  Thu 26 Nov 2015 07:29:21 PM UTC
Category:  Octave Forge Package Severity:  3 - Normal
Priority:  5 - Normal Item Group:  Matlab Compatibility
Status:  Fixed Assigned to:  None
Originator Name:  Open/Closed:  * Closed
Release:  * other Operating System:  * Any
* Mandatory Fields

Add a New Comment (Rich Markup)

Comment Type & Canned Response:


( Jump to the original submission )

Sat 19 Dec 2015 09:22:54 PM UTC, comment #13: 

My last patch has been pushed to the repo as d9df7552bf52. I think this bug report is solved now and can be closed.

Hartmut <hardy>
Fri 11 Dec 2015 12:56:28 PM UTC, comment #12: 

I've used the following script to have a look at the Matlab result for an automatic threshold result of our sample image:

clear, close all;
I = imread('sample2.png');
BW = edge(I, 'Canny');
imwrite(BW, 'ML2013b_autothresh_canny_sample2.png');

The resulting image is attached both

  • as image file, and
  • as a screenshot with a comparison between the Matlab result and the Octave result

(with all patches applied, including the one in comment #7).

My CONCLUSION: Matlab gives the same "not so nice" result when used without an explicit threshold value on this image.

So I would now strongly vote for option 3: APPLY ALL PATCHES to the Octave version of edge.m. This gives

  • good quality edge images as results (with an individually fine tuned threshold value)
  • a very good Matlab compatibility of the results, with and without explicitly given thresholds
  • a worse result on this single example image, when used without explicit thresholds

So the worse result  (with auto thresholds) on this sample image is the price we need to pay for improved Matlab compatibility.

Hartmut <hardy>
Thu 10 Dec 2015 09:40:06 PM UTC, comment #11: 

When I delete the "replicate" option in the imfilter calls of edge.m (with all my patches applied, as in comment #7) then the result of edge(I, 'Canny') is very similar to the original result without any patches applied. But a code change like this would not not a good option, as was discussed in bug #46570.

Now I very much expect the Matlab result of edge(I, 'Canny') to be as "bad" as our current result. But I will try to check this during the folowing days.

Hartmut <hardy>
Thu 10 Dec 2015 07:19:59 PM UTC, comment #10: 

Yes, I have the same observations as Carne.

Here is my reasoning about it:

  • The fix of the C-FUNCTION (my very first patch, never purely applied) was only to FIX a buggy behavior of the Canny implementation. The resulting countours weren't properly closed before. The change in the c-function fixed this very well. This fix has nothing to do with all the other following fixes in the m-file. Those m-file fixes all have a different aim: Make the behavior more Matlab compatible (which I think is necessary to add reasonable tests to the m-file). Conclusion: The change of the c-function should be beyond any doubt and stay.

For the various stages of the M-FILE (edge.m) I would like to summarize the effective changes between (A) the very first implementation (probably for years untouched), and (B) the m-file with my latest patch applied (from comment 7).

(A) "original" implementation of edge.m:

  • edge (I, "Canny", [5.2742e-004 0.3899]) gives a very good result in Matlab. It gives a very bad result in Octave. (This was the original statement in this bug report.)
  • edge (I, "Canny") gives in Octave a quite good result on this test image.
  • Conclusion: The interpretation of explicitly given threshold values was NOT Matlab compatible before. The guessing of an automatic threshold value was quite good, at least for this example image.

(B) "latest" implementation of edge.m with my patch from comment 7:

  • edge (I, "Canny", [5.2742e-004 0.3899]) gives nearly the same result in Octave as in Matlab. Both are good.
  • edge (I, "Canny") gives a not so good result in Octave. I do not know (maybe someone could check this...) what the Matlab result looks like in this case.
  • Conclusion: The interpretation of explicitly given threshold values is mostly Matlab compatible now. But the guessing of an automatic threshold value is worse as before, at least for this single sample image.

So currently we do not have both: We can either have a Matlab compatible interpretation of explicictly given threshold values. Or we can have a good (at least good for this single example image) guessing of automatic threshold values.

I would very much like to know the Matlab result of edge(I, "Canny") for this example image. This would make the decision easier!

I think we have the following options:
1. stick with (A): only apply the c-function part of my patches, otherwise stick with the old implementation
2. stick with (B): apply my latest patch
3. try to restore the automatic threshold guessing from the original implementation (A) (was not Matlab compatible, but better than now) but keep the Matlab compatible interpretation of thresholds as in (B). This might be doable, but somewhere I spoiled it...
4. try to figure out what Matlab does for automatic treshold guessing.  (I wanted to avoid this...)

I will think about about option 3. But I personally would also be very fine with option 2 (especially if Matlab also fails to deliver a nice edge image without explicit threhold values given).

What are your thoughts, Carne?

Hartmut <hardy>
Thu 10 Dec 2015 03:10:47 PM UTC, comment #9: 

This time, really with the screenshoot.

Carnë Draug <carandraug>
Project Member
Thu 10 Dec 2015 03:10:12 PM UTC, comment #8: 

I'm attaching the results I get using the sample image on the original bug report and no threshold values:

octave> I = imread ("~/Downloads/sample2.png");
octave> BW = edge (I, "Canny");
octave> imshow (BW)

The three figures are, from left to right:

1. the old result of edge before any change
2. with cset cb24795f0d8f which was to fix bug #46099
3. after the patch on comment #7

While 3 is a big improvement over 2 , it is still much behind what the function did originally on its own.

Carnë Draug <carandraug>
Project Member
Tue 08 Dec 2015 09:02:20 PM UTC, comment #7: 

Please find attached a new PATCH. It includes the mentioned changes of my last two posts in a more orderly form.


  • test this patch if it improves the situation of this bug report
  • has any other side effectcs

I have observed that this patch mostly fixes the original bug report:

  • The resulting edge image is already quite nice if you give the explicit threshold values (values probably chosen for Matlab) of post 1.
  • If you then fine tune the threshold values for Octave, e.g. to threshold = 0.3, then you will get results that are as good in quality as the Matlab results (but not identical).
  • If you dont't give any thresholds manually, then you will get a worse result (I don't know what Matlab will give you, but probably also a worse result). But you can already anticipate the correct edge contours and only need to fine tune the manual threshold values (which is the normal workflow).

The main remaining difference to the Matlab implementation of the Canny edge detector seems to be the automatic guessing of threshold values, if the user doesn't give them explicitly. Octave currently guesses those automatic thresholds values via the mean of the edge strength in the image. I think that Matlab has some algorithm for this to have a certain percentage of the image pixels as edges. But as I said before I don't think it's worth to perfectly mimic this Matlab behavior as well.

(file #35672)

Hartmut <hardy>
Mon 07 Dec 2015 10:24:41 PM UTC, comment #6: 

(This drives me crazy...)
Could someone please test, if the script of the original bug post gives a roughly Matlab compatible result, if you do the following change in edge.m?

-      Es = sqrt ( Jx.^2 + Jy.^2 );
+      Es = sqrt ( Jx.^2 + Jy.^2 );
+      Es_max = max(Es(:));
+      if Es_max > 0
+        Es = Es ./ Es_max;
+      endif

(I think I've seen this work fine. But I start mixing up file versions now...)

This change is ment to be used additionally to the introduction of some kind of im2double at the beginning of the Canny routine. (see last post)

Hartmut <hardy>
Mon 07 Dec 2015 08:47:48 PM UTC, comment #5: 

Oops. Your are right!

Sorry for the error in my patch, this seems to be about the DATA TYPE of the input image. In the original implementation of the gaussian filtering in edge.m there was an "double(im)" and in my new implementation I take the input image directly, without converting it to double before. (Funnily my two new tests passed anyways...)

It works if you make the following change to edge.m:

-      J = imfilter (im, gauss, "replicate");
+      J = imfilter (double(im), gauss, "replicate");

Maybe you know a more clever way to convert the input image to double values as a first step, then just do it like this :)

And maybe we should also add an additional test to check that also integer typed input images are treated well?

Hartmut <hardy>
Mon 07 Dec 2015 08:23:20 PM UTC, comment #4: 

I just tested this with the patch from bug #46099 and that patched seems to have made things a lot worse.

Previously, not setting threshold manually was able to improve it a lot but after that patch that won't work anymore.

Carnë Draug <carandraug>
Project Member
Sat 05 Dec 2015 09:40:31 PM UTC, comment #3: 

I've just filed a PATCH to fix the issue of bug #46099. I suspect that this could fix the issue in this bug report as well.

Could someone please test, if the mentioned patch improves or solves the situation here as well?

There might still be some difference in the automatic computation of threshold values, if you don't give them explicitly. But otherwise I hope this  patch will make the Canny method in edge.m behave much more like its Matlab counterpart.

Note: Further investigation showed, that Matlab does NOT use LoG filtering as a first step in the Canny edge detector. They weren't saying this clearly in their documentation, but now I'm pretty sure they are also doing a Prewitt filter as a first step. I think they only had a bug in their old implementation (now in "canny_old" method). Sorry for the confusion!

Hartmut <hardy>
Fri 27 Nov 2015 09:48:53 AM UTC, comment #2: 

Thank you for your answer and tip, indeed it is a "feature" and not directly related to issue #46099. I've tested with an empty threshold vector and it works much better now.

Thu 26 Nov 2015 07:32:54 PM UTC, comment #1: 

And here is my personal comment on it:

If you run this script for example with "thresholds = []" then you will get at least a visibly reasonable result. (The result will still have un-closed contours, which would most probable be fixed with the supplied patch of comment #1 in issue #46099).

The root cause of this (Matlab incompatible) behavior is that in the first calculation step of the Canny edges

  • Octave calculates the gradient with a Prewitt filter, whereas
  • Matlab calculates the gradient with a LoG filter (if we believe their documention). Matlab also states that they changed this some time ago. Before Matlab R2011a they also used a Prewitt filter for the first step.

This different calculation step for the initial edge pixel candidates explains, why currently Octave and Matlab interpret the "threshold" input differently. And I think this is the reason why this anonymous user gets results he doesn't like.

Hartmut <hardy>
Thu 26 Nov 2015 07:29:21 PM UTC, original submission:  

I moved this away from bug report #46099 (comment 2 of an anonymous use), because it is a different and new topic. The following is a quote:

I'm evaluating an image binarization code (originaly developed for Matlab) with Octave (4.0.0) and image package (2.4.1). After some investigation on why I'm getting worse results with Octave than with Matlab (R2012b), I've found that edge function with 'Canny' method outputs a much noisier result with Octave than with Matlab. In order to give an example, I've run the following code with both software:

clear, close all;
I = imread('sample2.png');
thresholds = [5.2742e-004 0.3899];
BW = edge(I, 'Canny', thresholds);
imwrite(BW, 'result_edge_canny_sample2.png');

As one can see in the attached files, "Octave_result_edge_canny_sample2.png" is much noisier than "ML2012b_result_edge_canny_sample2.png". As a result, the binarized image (generated by the image binarization code, which uses edge function with 'Canny' method) is also noisy and almost useless.

Hartmut <hardy>


(Note: upload size limit is set to 16384 kB, after insertion of the required escape characters.)

Attach Files:


Depends on the following items: None found

Items that depend on this one: None found


Carbon-Copy List
  • -email is unavailable- added by hardy (Submitted the item)

    There are 0 votes so far. Votes easily highlight which items people would like to see resolved in priority, independently of the priority of the item set by tracker managers.

    Only project members can vote.





    Follow 10 latest changes.

    Date Changed by Updated Field Previous Value => Replaced by
    2016-07-14 carandraug StatusNone Fixed
        Open/ClosedOpen Closed
    2015-12-11 hardy Attached File- Added ML2013b_autothresh_canny_sample2.png, #35698
        Attached File- Added Compare_left=Matlab2013b_right=Octave+AllPatches.png, #35699
    2015-12-10 carandraug Attached File- Added Screenshot from 2015-12-10 15:06:09.png, #35685
    2015-12-10 carandraug Release4.0.0 other
    2015-12-08 hardy Attached File- Added edge_canny_fix-integers-etc.patch, #35672
    2015-11-26 hardy Attached File- Added sample2.png, #35565
        Attached File- Added Octave_result_edge_canny_sample2.png, #35566
        Attached File- Added ML2012b_result_edge_canny_sample2.png, #35567

    Back to the top

    Powered by Savane 3.9