New G'MIC filter: DCCI 2x resize (updated)

This sounds like a good filter to quickly add into PhotoFlow, for image up-scaling!

Since in my case the filter will be parallelized and applied to image tiles, I would at some point need to know the precise tile padding required by this algorithm. Any idea?

Also, would you have any good test images to share, so that I can compare the result with the default “no halo” method used in photoflow?

Thanks for your great work, Andy!

Thanks Andy, here is my version. I’ve done a straightforward implementation of the algorithm presented in the link you gave.

Feel free to give feedback !

Ah, it seems I have some issues on the right/down boundaries.

EDIT : OK this one should be better : http://pastebin.com/ijbWWZBf

Aha, that’s probably how I should have prototyped it in the first place! Now the two can be compared, I notice there are very small differences in the output - could be rounding errors or simply me making mistakes but at least I can check it. Since the math parser version seems slower I wonder if the two can be combined?

I didn’t distribute the interpolated pixels until the end, which means the kernel is smaller but it makes it harder to get my head around.

@Carmelo: I think it would need overlap of 2 pixels (possibly 3) since the largest kernel I use is 5. As for test images I can maybe do some later, otherwise there are some b&w examples linked on the wiki or you can test it with gimp + g’mic.

Andy, I’ve done that primarily to test the robustness of the new features of the math parser.
If your version is faster, then we probably should stick with it instead.
That would be cool anyway to obtain the same results, as well as the same image size at the end
(i.e. 2width-1 x 2height-1). But of course, I’ll vote for the fastest version!

Cool, my task now is to compare output for correctness and tidy up the code. I have some ideas where differences might happen due to precision errors; it’s possibly where I optimised the math for fewer calculations but speed loss should be minimal if I have to change it. Luckily I have some time now :smile:

Don’t worry, if you do not have something ready at hand I’ll take some of my images and do some experimenting. Just wanted to see if you had examples that clearly show the possible artifacts… probably some sharp shot of tree branches might be a good candidate to see the staircase artifacts?

There might be an issue with the math parser version - it almost always has worse PSNR results than mine even in greyscale so I did a comparison; there are large differences within a 3 pixel boundary around the edges.

Repeating the tests after removing a 3px border for all images shows the math parser with better PSNR, but only a very small amount (~ 3x10^-6). With colour images it still does worse due to not averaging gradients sums across channels.

Edit: suspect the smoothing step in the wiki article may have the pixel values the wrong way round, need to compare properly with the paper / matlab example.

Can confirm the matlab source code doesn’t seem to match the wiki page as far as the “Smooth Area” sections are concerned (i.e. the wiki appears to be wrong). The weights match up but the interpolated pixel values are the wrong way around.

With that patched the k exponent actually affects output, I might have to add a slider for it since there’s more edge smoothing overall now. For now I’ve just patched the filter as it stands… will have to leave the rest for later.

Ah, that’s interesting !
Maybe I’m asking too much, but do you think the fix could be simple using the math parser instead (my version).
I’ll be interested to understand what differences it adds.
Anyway, I’m interested to put your faster version directly in the g’mic stdlib, if you agree with that idea.
Does it work when you have an arbitrary number N of channels ? Does it work for 3d volumetric images ? (i.e. just double the width and height of all planes ?).
Thanks!

The fix is very simple for both methods, if we take a sample from the math parser (I’ve omitted the boundary for brevity):

 DownRightPixel = (-j(-3,-3) + 9*j(-1,-1) + 9*j(1,1) - j(3,3))/16;
 UpRightPixel = (-j(3,-3) + 9*j(1,-1) + 9*j(-1,1) - j(-3,3))/16;
 DownRightPixel*weight1 + UpRightPixel*weight2

The quickest way is to switch the numbers of weight1 and weight2 but more correctly it should read:

 DownRightPixel = (-j(-3,-3) + 9*j(-1,-1) + 9*j(1,1) - j(3,3))/16;
 UpRightPixel = (-j(3,-3) + 9*j(1,-1) + 9*j(-1,1) - j(-3,3))/16;
 UpRightPixel*weight1 + DownRightPixel*weight2

Where we take “UpRightPixel” to be the 45 degree interpolated value. The same needs to be done for the horizontal/vertical pass. In the other method it’s a case of referencing two images the other way around so every bit as easy.

Arbitrary channels is easy enough, in fact only support for opacity changed that. What I should really do is have a “native” command which doesn’t care about channels/opacity, then for the g’mic filter split the opacity and call the native command on those. That has to be done because of gradient averaging across channels.

3d volumetric :grinning:
Hahah ok well without alterations it would work as is but with no interpolation between “slices”. Assuming you do a third pass in the depth plane it would be interpolating between previously interpolated values… it needs some thought!

Adding to g’mic stdlib is a great idea, but I still have cleanup to do. For instance I do a third unnecessary gradient amongst other things, it just made it much simpler to deal with in my head. If you fancy doing the tidy up for me though I’m more than happy :smile:

I forgot to answer about the changes the fix produces! It’s mostly quite subtle but here’s a list:

More smoothing in general. Previously higher threshold gave more noticeable jaggies, due to the “Smooth Area” pixels being in the opposite direction from the edge.

Exponent k has more effect. It’s actually quite difficult now to see staircase without reducing k.

PSNR is improved. This is something I’ve never tried before so could well make mistakes; scale to 50% by nearest-neighbour and scale back up then compare after crop (since I now include the 1px removal) using g’mic -psnr. This is what made me spot it - decreasing k actually improved PSNR which made no sense.

Edit: I’d really like somebody to check what I’m saying! Especially compare wikipedia with the original paper/matlab source.

1 Like

I hope somebody can confirm this, but I now think the wiki page has another issue… the description of the horizontal/vertical gradient seems to be wrong.

The original paper appears to mention a 5x5 kernel with 7 absolute gradients summed in each direction. The matlab source also shows 7 subtractions per direction, whereas the wiki shows 9 in a diamond shape (within a 7x7 kernel).

This might explain some issues I’ve noticed: PSNR for the G’MIC filter being best around threshold 1.5, comparison with matlab test output images being ever so slightly different and there being quite a lot of edge smoothing in general.

It’s probably best to stick to the paper so I’m going to compare both (i.e. redo the filter).

That’s awesome you notice all these stuffs. Very surprising to see so much mistakes in the wikipedia page! Maybe it should be good to fix them also in wikipedia.

Is there some way to make this produce W2xH2 pixels exactly (even if just by duplicating a column + row?)?

have my eye on this to use as an antialiasing filter (DCCI 2x → scale down by a factor of 2 → smoothed image), but I currently am unable to avoid offsetting the result by half a pixel, which AFAICS is caused by having an input for the downscaling that is not an exact integer multiple of the target dimensions.

Yes, you’ll end up with a row at the right and bottom edges which has been interpolated with pixels outside the image boundary. As you say, duplicated edge would probably look best (i.e. neumann). I can add an option in the next version to allow that.

Bad news is it might have to wait a little, I now discover the matlab source has errors too - horizontal and vertical are swapped, but luckily swapped in all places so the final output is correct! Some of the math appears to be redundant too (taking ratio of reciprocal of gradient sums instead of just using ratio of gradient sums swapped).

I’m really glad I released this as “experimental” right now :smile:

Updated, changes:

  • 5x5 H/V kernel (from original paper)
  • Exponent slider
  • Option to extend 1px
  • -gcd_dcci2x operates on arbitrary channels, separate filter command now deals with opacity and values cut

Extra info:

  • PSNR of 7x7 H/V kernel is frequently higher even though it’s not mentioned in the paper, might be worth having an option to allow it.
  • Instead of 1/(d^k+1) I just use (d^k+1) and swap the values.
  • Differences still remain between filter output and matlab examples, especially in low gradient (“smooth”) areas. Not sure why… PSNR now very close to values in the paper.

G’MIC math parser equivalent, without gradient averaging across channels: http://pastebin.com/9iU3sarw

2 Likes

Hello Andy,
I’ve integrated your code in the G’MIC stdlib. Thanks a lot, that’s really great.
Here are some examples and comparisons of what I get with this new upsizing algorithm:

The filter have been put in the Repair/ section of the G’MIC plug-in for GIMP:
<img src="/uploads/default/original/1X/a48e8f2c7349c41e12f0a0c2c7c5868404dd6f86.png" width=“690”

And here are a few results for x4 upscaling:

height=“409”>

3 Likes

I was playing with this and had a thought.

Instead of interpolating between values, it would be better to estimate the sub-pixels that would make up each pixel if the image had been downsized.

So I came up with this to improve the dcci upscaling, or perhaps it is better called something like ‘targeted sharpening’.

--scale_dcci2x[0] $1,$2,1
--resize[1] [0],2  
-sub[2] [0]  # find the differences between the original and the half-sized upscaled version.
-scale_dcci2x[2] $1,$2,1  # upscale the differences 
-mul[2] -1
-add[2] [1]  # add them together
-rm[1]

# repeat but with image rotated 180 degrees to remove slight directional bias

-rotate 180  


--resize[1] [0],2
-sub[2] [0]
-scale_dcci2x[2] $1,$2,1
-mul[2] -1
-add[2] [1]
-rm[1]

-rotate 180  # rotate back

-c 0,255

2 Likes

Hello

Great stuff in here!
I just wanted to ask that when you show demonstrations of what the algos can do, that you also use them on images which don’t suffer from JPEG compression artifacts. All of the photos from David’s previous post have such artifacts which makes the upscaling demonstration void, at least as far as I’m concerned (though of course some people will upscale JPEGs).