General edgedetection filter that can use the filtermasks/layers.
ClosedPublic

Authored by woltherav on Jul 7 2017, 3:27 PM.

Details

Summary

Okay, so... edge detection filter.

My initial impetus to work on this was that I was looking at Inkscape's svg filters, and thinking to myself, "darn, that specular looks awful", and that's because the heightmap to normal map code isn't stellar.

And then I realised we still had this issue with our own heightmap code in the phongbumpmap, and there's also been a request to make a general heightmap to normal map filter.

Now, the basis of a heightmap to normal map filter is a edge detection convolution filter. Our current sobel filter is a little old, and thus lacked the systems to make it a proper filter layer, like the gaussian blur did, so that gets up to this patch.

We have here a "Edge Detection" filter that can do prewit, sobel, and the super-simple cousin of sobel based on the old sobel filter. The larger sizes are determined based on the 4th answer noted here: https://stackoverflow.com/questions/9567882/sobel-filter-kernel-of-large-size (I wanna try the third answer as well at some point, but I am still trying to determine how kernels really work, so number 4 was easier to implement).

Now, the patch isn't complete, I have several questions:

@dkazakov:

  1. The maths in the filter doesn't really match up with the old sobel filter. The old sobel filter, amongst others...
    1. In the old filter, the two horizontal versions are subtracted from on another, same for the two vertical versions. Then, the horizontal and vertical version have the pythagoras theorem applied to them for mysterious reasons. Should I copy this fully(Right now just using addition)? I would then need to make a seperate blending mode that does a pythagoras equation to the src and dst, would I not? Is this of any use at all? [DONE]
    2. The result is also divided by a certain value. I think I know where this comes from, it's mentioned at the end of the third answer in that stack overflow post. How would I go about applying this division? Make a colortransform? Or just abuse the division blending mode? (Like, fill a device with a color of value 8, apply division blending mode, see what comes out)
  2. I would like to make a variation of this that instead of overwriting the whole device, it makes a grayscale version and writes the result into the alpha of the device. This is much more useful to artists, as it gives a thin outline to their colorpatches. How would I go about writing the grayscale result that is in a seperate paint device into the alpha channel of the main paint device when I only have kispainter and kispaintdevice to my disposal? (And, technically, is there a method for doing this with any of the channels? That'd be necessary for the normal map filter which I'll make later :) ) [DONE]

@boud: I am having trouble determining how to load the filter type into the comboboxes. Like, filtertype to config goes fine, but the result is a bit... flaky? How can I make this robust as possible? [DONE]

I am also thinking.. theoretically, I could make this filter encompass the old left/right/top/bottom edge detection on top of the old sobel filter, but I think that would need either a) 4 sliders, or b) two sliders and a toggle for left/right/both and top/bottom/both? Would this be sensible?

Maybe I should divide the filter UI into two tabs: one that is interesting to artists, and an advanced tab, for these kind of fiddly features that are there for backward compatibility.

Any other comments are welcome too.

Test Plan

It's tested, it works.

Diff Detail

Repository
R37 Krita
Lint
Automatic diff as part of commit; lint not applicable.
Unit
Automatic diff as part of commit; unit tests not applicable.
woltherav created this revision.Jul 7 2017, 3:27 PM
dkazakov added a comment.EditedJul 20 2017, 12:13 PM

Hi, @woltherav!

  1. In the old filter, the two horizontal versions are subtracted from on another, same for the two vertical versions.

I guess the explanation is the following:

  • the derivative can be both, positive and negative, but in most of the cases, the channels in the paint device can hold only positive values.
  • therefore, old filter tried to apply right-to-left and left-to-right kernels and then sum them up somehow.

Speaking truly, I'm not sure if it is mathematically correct, because it normal subtraction of rtl-ltr kernels should always generate empty image...

As far as I can tell, you can skip this two-pass (rtl+ltr) approach and just modify the kernel accordingly using KisConvolutionKernel::offset() and KisConvolutionKernel::factor() somehow. Basically, you need to scale range [-255; 255] range into [0; 255], so the offset should be 128, and factor 0.5.

That will generate a denormalized device for you that can be reused later, when merging X and Y passes.

Then, the horizontal and vertical version have the pythagoras theorem applied to them for mysterious reasons. Should I copy this fully(Right now just using addition)? I would then need to make a seperate blending mode that does a pythagoras equation to the src and dst, would I not? Is this of any use at all?

Well, the necessity of Pythagorean theorem can be explained by the following:

  • the derivative is basically a vector, which has length and orientation in space
  • so the user might want to have four properties of that derivative (sobel, prewitt are just different methods of calculation of derivative in discrete space):
    • growth of luminosity in X direction (your l-t-r kernel)
    • fall of luminosity in X direction (your r-t-l kernel)
    • growth of luminosity in Y direction (your t-t-b kernel)
    • fall of luminosity in Y direction (your b-t-t kernel)
    • length of the derivative vector (length = sqrt(max(abs(device_after_ltr), abs(device_after_rtl))^2 + max(abs(device_after_ttb), abs(device_after_btt))^2))
    • direction of the vector in radians (see formula here, though I'm not sure the user wants it)
  • in most of the cases the user wants the length of the derivative, that is the one calculated with Pythagorean theorem
  • if you have two denormalized devices as described above then you can find the length using the formula: length = 2 * sqrt ( (denorm_x - 128)^2 + (denorm_y - 128)^2)

All these calculations can be done in a usual iteration using KisSequentialIterator. Creating a full-features composite op or transformation for this purpose is really an overkill. Though you will have to handle all the different colorspace format yourself.

  1. The result is also divided by a certain value. I think I know where this comes from, it's mentioned at the end of the third answer in that stack overflow post. How would I go about applying this division? Make a colortransform? Or just abuse the division blending mode? (Like, fill a device with a color of value 8, apply division blending mode, see what comes out)

You can use KisConvolutionKernel::factor() for this multiplication. Though you should take care about the normalization of the color spaces (quint8, quint16, half and float).

  1. I would like to make a variation of this that instead of overwriting the whole device, it makes a grayscale version and writes the result into the alpha of the device. This is much more useful to artists, as it gives a thin outline to their colorpatches. How would I go about writing the grayscale result that is in a seperate paint device into the alpha channel of the main paint device when I only have kispainter and kispaintdevice to my disposal? (And, technically, is there a method for doing this with any of the channels? That'd be necessary for the normal map filter which I'll make later :) )

Check KisNodeManager::slotSplitAlphaWrite() about the example of implementation. I guess, you can even reuse some code from it :)

woltherav updated this revision to Diff 20112.Sep 29 2017, 6:26 PM
woltherav edited the summary of this revision. (Show Details)
woltherav edited the test plan for this revision. (Show Details)

I switched to the factor and offset using variation @dkazakov suggested, with the mixing being handled by a pythagoras theorem. I also added a toggle for only writing the result into the transparency.

woltherav updated this revision to Diff 20115.Sep 29 2017, 6:46 PM

Small fix added for alpha. Old version looked glitchy when the original color was semi-transparent.

woltherav updated this revision to Diff 20315.Oct 3 2017, 7:11 PM

This finalizes the edgedetection filter.

It can now do 3 formulas(prewitt, sobol, simple), 5 outputs(all, left, right, top, bottom, radian), seperated radii, and output the result to the alpha.

I cannot think of anything else I should be doing with this. Please test/review?

looking pretty good to me. This code looks quite mathematical, so I can't really speak to that. There is a flicker that is happening when changing settings, going between the original and the applied settings. Not sure if that could be reduced at all as it... That is pretty minor though

I am not sure what the results are supposed to look like, but they appear to make sense with the results and things don't crash.

rempt accepted this revision.Oct 4 2017, 8:55 AM
rempt added a subscriber: rempt.

Works really well :-)

This revision is now accepted and ready to land.Oct 4 2017, 8:55 AM
This revision was automatically updated to reflect the committed changes.