Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

libpng 1.6.2 not working properly with AGG / MapServer 6.2? #4722

Closed
r-robert opened this issue Aug 8, 2013 · 15 comments
Closed

libpng 1.6.2 not working properly with AGG / MapServer 6.2? #4722

r-robert opened this issue Aug 8, 2013 · 15 comments

Comments

@r-robert
Copy link

r-robert commented Aug 8, 2013

Hello all,

I noticed that the output of legend and/or shp2img commands is quite different in my new setup (libpng 1.6.2 + MapServer 6.2) compared to what I had before (libpng 1.2.43 + MapServer 5.0).

The problem is related to PIXMAP symbols. The output is quite bad in the new setup, they just aren't as clear as they were before.

I attached a ".zip" file (zip extract password: libpng ) containing sample data to be used for testing purposes only. If you unzip the archive, from the resulted directory you can run the "shp2img -m test.map -o any_name.png -all_debug 5" command. It should work with all the paths in the mapfile. I used the same mapfile in both environments for testing purposes.

I also put the resulted ".png" files:

test_bad.png - was obtained using my new setting: libpng 1.6.2 + MapServer 6.2.1; it also displayed some errors related to sRGB profile;

test_good.png - was obtained using the old settings: libpng 1.2.43 and MapServer 5.0; clean output, no errors in the command line.

First I thought that maybe it has something to do with the libpng files but shooting an email regarding this issue to their mailing list ( http://goo.gl/XCjLLg ) I got an interesting answer:

Quoted Message:

Ok, I'll only post cropped sections of the images in this reply. My shp2img does not work at all, however this is moot because the problem is obvious from the output images (these are scaled up by a factor of 14 to make the problems obvious):

Inline image 1 (see attach)

The left image is a correctly resized version of one of the original symbol images. In the output files the symbols (which are stored as 299x299 24-bit RGBA PNG files) are resized to 16x16.

The middle image is from the old output ("test_good.png") and the right image is from the new output ("test_bad.png"):

There are gross errors in the resampling (resizing) code in the version of mapserver (shp2img) that you are using. While the original (mapserver 5.0) code was not correct either - I can see aliases in the test_good.png image - it was a lot better than the resizing in test_bad.png where there are obvious dropouts. For example the dark pixels at the top and right of the red cross suggest that arithmetic overflow is happening in the calculations.

This is apparently a change in mapserver, however I notice that the 'good' output is an interlaced PNG while the 'bad' output does not have interlacing. In the test.map file you sent me the OUTPUTFORMAT entries all have 'FORMATOPTION "INTERLACE=ON"' and the shp2img command does not have a '-i' option:

shp2img -m test.map -o any_name.png -all_debug 5

So the problem could be a change in the interpretation or defaulting of the output file format when there is no '-i'.

It might also be a change in the default resampling filter (resizing code) used. The AGG renderer used in mapserver apparently has support for a large number of different filters - maybe the default changed? All the same, no filter should product dropouts like those seen in test_bad.png, so I think there must be a bug anyway.

The bad iCCP profile in the original symbol files doesn't affect this. In fact I think the color space information is being ignored completely - this would explain the errors in the test_good.png image.

End Quoted Message

I appreciate any help I can get,

Inline image:
shp2img

Zip File (extract password: libpng ):
http://speedy.sh/GqvYH/libpng16.zip

@ghost ghost assigned tbonfort Aug 8, 2013
@tbonfort
Copy link
Member

tbonfort commented Aug 8, 2013

interesting find... I believe the artifacts you are seeing are happening because you are way out of bounds of the designed use-cases for pixmap symbols; mapserver uses bilinear resampling when scaling images, which fails miserably for the scaling you are applying to your input pixmaps. We might look into adding additional filters when the scaling is far away from the designed limits (i.e. a scaling by a factor of around 2), however this isn't high priority as you should not be scaling or rotating pixmap symbols in the first place, for questions of quality and performance.
Until that happens, you have two possible workarounds:

  • if you are planning on using multiple symbol sizes or applying symbol rotation, use a scalable symbol instead of pixmaps: i.e. truetype or svg
  • if not, downscale your input image with an image manipulation program applying the filters that produce an acceptable result, and use them in mapserver without any scaling or rotation (i.e. don't set SIZE or ANGLE in your STYLE block).
  • if you absolutely want to keep pixmap symbols with scaling, at least downscale it in an image manipulation program down to a size that's roughly equal to your desired output size (i.e. in your case to something around 30x30 pixels)

@tbonfort
Copy link
Member

tbonfort commented Aug 8, 2013

and for the record, this has effectively nothing to do with libpng, and the "dropouts" that are seen are very probably not due to arithmetic overflows, but the resampling picking one of the gray outlines from the original png.

@r-robert
Copy link
Author

r-robert commented Aug 8, 2013

Thomas,
Ok, thank you for the reply. I was just wondering why in the previous environment it did a good job and now it does not.

I must say that in the example we have a scale of >100,000 but I does that at every scale till < 1000 whilst before it rendered the symbol clearly.

@tbonfort
Copy link
Member

tbonfort commented Aug 8, 2013

@r-robert it was not doing a good job, it was doing a better job that was still far from satisfying :). Your old examples are using the GD renderer, the AGG pixmap resampler has always behaved as today. As for your map scales, they are irrelevant here; the scaling I'm referring to is the downsampling from a 299x299 pixmap to a 21x21 (or 25x25, 30x30, etc) one.

@r-robert
Copy link
Author

r-robert commented Aug 8, 2013

@tbonfort I just checked with a re-sized version of the icons and it looks ok.
Thanks for the feebback and I guess this topic can be closed.

@r-robert r-robert closed this as completed Aug 8, 2013
@r-robert
Copy link
Author

r-robert commented Aug 8, 2013

@tbonfort just a quick follow-up:
with re-scaling the images work for one, maybe 2 zoom levels, but at zoom 1000 a symbol sized 30x30 is too small without a SIZE specified. Using SIZE the image is larger but is lacks the clarity.

I would like to know if I can make use of the GD renderer in this case like before (although I think it is going to be removed?).

I am asking because I have more than 400 png symbols on a bunch of maps. I used them this way because it worked thus I would prefer to make it work like it did instead if replacing all the png files.

@tbonfort
Copy link
Member

tbonfort commented Aug 8, 2013

You have many ways to treat this, even if you insist on using pixmap symbols which are not designed for this. (e.g. create an imagemagick script that resizes your pixmaps to 20x20, 30x30 and 40x40). You can still use GD with 6.2, although it is basically unmaintained and is being dropped completely in the next release. Having mapserver rescale a 300x300 image down to 20x20 for each feature is also very inefficient.

@jbowler
Copy link

jbowler commented Aug 8, 2013

Dropouts: the filter kernel size is determined by the lowest sample rate. In the case in question the input and output widths are 299 and 16 respectively. The filter is bilinear, so the width is 1 in the output and 19 in the input. That means that only the outermost pixels in the output had the case where the filter overlaps the edge of the image. One classic filtering bug is not to take this into account - the filter weight has to be calculated specially for the edge because the sum of weights no longer adds up to 1.0, however I don't think this is happening because the dropouts are 3 pixels into the output and are not symmetric. Nevertheless I couldn't see where the AGG 2.5 code adjusts the filter weight (I didn't look very hard.)

Unsymmetric output: that suggests and off-by-half error. The only way I know to get this right without excessive complexity (which leads to errors) is to regard input and output samples as sited at (0.5,0.5) in their own sample space. This means that the edge pixels of the output end up with alpha, but then this must happen if the image size is not to be changed. (So the input sample at (0.5,0.5) is mapped to output symbol at (0.5,0.5) and the input sample at (w_input-0.5,h_input-0.5) is mapped to the output at (w_output-0.5,h_output-0.5; this is slightly counter-intuitive but it does actually avoid scaling by up to (w-0.5)/w!)

Use of bilinear: bilinear performs the same whether scaling up or down, indeed it often looks a lot better when scaling down (as in this case). In fact it's a lousy filter; bicubic is so close to exact (the sinc filter is exact) that it's pointless using any other. There's often hardware support for something like bicubic. The result of using bilinear is that the images blur when scaled up and produce odd aliases when scaled down.

Correct resampling: the aliases I was seeing in the original image suggest to me that the AGG code is filtering in an encoded space. This does not work and produces errors, regardless of filter, on a par with those in bilinear. Sample date must be converted to a linear space before resampling. Notice, also, that alpha data must be multiplied out; PNG stores alpha samples with the original colors (required for color correction), so each sample must be multiplied out after the color correction and before scaling:

sample := decode(sample) * alpha

In the case in question where the input is sRGB this is pretty accurate (i.e. the arithmetic errors are small, though if you were to use a sinc() filter then doing the correct inverse-sRGB transform might be worthwhile):

sample := sample^2 * alpha
... rescale samples ...
... do alpha composition ...
new_sample := sqrt(new_sample)

Since the gamma (or sRGB encoding) has to be inverted before doing the alpha composition, and alpha has to be multiplied out, it's just a matter of doing everything in the correct order. I don't know what is going wrong in the original case, but something seems to be up.

Finally; is it really bilinear? I don't think so, looks more like Fant to me, because the 'bad' image has apparently been resampled with a block filter. That's what Fant degrades to on large down-scales.

John Bowler jbowler@acm.org

@r-robert
Copy link
Author

@tbonfort I understand better now. Considering all this, don't you think that it would be a good addition to add better functionality for these types of data?

It's true that I am considering my working environment but the maps really look nice when you put an icon to represent something. In this case, it is easier (for us) to deal with png files.

It would be a great addition if the resizing could be done well in mapserver compared to the other option of having 3 png files representing the same thing.

I am unaware how difficult it would be though to add this extra functionality.

@jbowler
Copy link

jbowler commented Aug 21, 2013

I second the last r-robert comment; downscaling large images correctly certainly involves much more CPU/output-pixel but I feel that is a much better compromise that downscaling incorrectly and achieving better CPU/output-pixel.

@tbonfort
Copy link
Member

@jbowler, thanks for taking the time to write your previous explanation. @r-robert I'm still not convinced that using pixmap symbols is the best solution when rendering symbols in multiple sizes, given mapserver has two alternative symbol types that perform correctly for this use-case (namely svg and truetype). I however agree that there is room for improvement in this area. Is this something that you would be able to fund, either by contributing and then committing to maintain a patch for this, or by contracting me or another mapserver developer to do it for you ?

@r-robert
Copy link
Author

@tbonfort I think I will let this float on the issue list for a while as it is out of my reach to fund this functionality for the moment.

Maybe other users will pick up the idea and something will eventually be done.

Thank you all for the info,

@r-robert
Copy link
Author

@tbonfort one thing I could do is to contribute to the MapServer wesbite, like translating documentation or something like this. Would this work, or not really?

@tbonfort
Copy link
Member

@r-robert I'm not sure what you are trying to imply here... if you want to contribute to the doc site, be it by adding a comment about the (non)scalability of pixmap symbols, or any other contribution, then that will be greatly appreciated. However those contributions won't buy you the implementation of the scaling you need; we open-source developers are somewhat like other people, and also need to put food on our table, which is achieved by getting payed in hard money for those kinds of task :)

@r-robert
Copy link
Author

@tbonfort I was not trying to imply anything, just to see if I can make this happen if I do not have funds or enough programming skills (yet);

At first, I considered this issue more like an error that could be fixed instead of something new that should be added, because like I already said, the many years in which I worked with MapServer 5.0.0 it worked well. If it would have never worked well, maybe now I would have 400 svg icons instead of 400 png icons.

Enough said, thanks for the info and support,

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants