jankovicsandras / imagetracerjava

Simple raster image tracer and vectorizer written in Java for desktop
The Unlicense
134 stars 49 forks source link

Incorrect results? #1

Closed MiYanni closed 8 years ago

MiYanni commented 8 years ago

When I run your code, I get this result for smiley.png: Traced smiley

Similarly, 13.png in the testimages folder does not look correct. The colors are wrong and lots of little fragments are present. This is what it looks like: Traced 13

I've converted this code to C# (https://github.com/MiYanni/ImageTracer.NET) and ran 13 against that. I feel there is generally some kind of color issue, because here is the output I get from that: C# Traced 13

Is there something wrong with the Java version? I'm simply trying to figure out how the tracing should function. Let me know if I need to look at the JS version to get the right output. Thanks!

jankovicsandras commented 8 years ago

Hi,

1.

When you run ImageTracer with the default options, this is the correct result. The logo image is actually made with these (or similar) options:

java -jar ImageTracer.jar smiley.png outfilename smiley3.svg ltres 0.1 qtres 1 scale 10

This is by design. Some result SVGs will look better with more straight lines (big ltres, small qtres, e.g. ltres 1 qtres 0.1), others with more curves (small ltres, big qtres, e.g. ltres 0.1 qtres 1). I recommend to experiment with different options and find what's best for the input image.

2.

Similarly, the default palette has only 16 colors, and the color quantization is randomized (not deterministic) by default (but can be forced to be deterministic). Sometimes it can't find good enough colors. I suggest to try setting numberofcolors to e.g. 64 and run the tracing multiple times. Selective Gauss blur and bigger pathomit (e.g. 16) might help with the fragments.

3.

The problem might be that C# handles both Bitmap and byte datatypes differently than Java. I don't know this, because I have very little experience with C#, but its Bitmap might use a different byte order (ARGB instead of RGBA or the other way around?). Also C# byte range is 0..255 but Java byte range is -128..127.

Please check these in https://github.com/MiYanni/ImageTracer.NET/blob/master/ImageTracerNet/ImageTracer.cs

Thank you for making a C# port, great work! ☺

MiYanni commented 8 years ago

1. Awesome. Thanks! I was wondering how the options work. Based on this, I think creating an interactive GUI for this algorithm would help visualize (in real time) the effects of the options on the output. That idea is something I'm considering making.

2. Based on this, it makes the idea in 1. even more valuable. You could show what value(s) was used for quantization for each render. So, eventually, you might find a group of settings you really like. Also, being able to save those settings (XML, JSON, etc) would be fantastic. If you need an example sampling app, look at the app ScalerTest for the xBRZ image scaling algorithm (https://sourceforge.net/projects/xbrz/). That's the kind of app I'm visualizing in my mind.

3. In my BitmapExtensions.cs file, I've written a few methods that convert Bitmaps to/from int[](and can do byte[] also). But, I'm finding values I'm not expecting. So, I've been looking into why that is happening. Your comment about Java byte range leads me to believe it might be a signing issue.

Awesome feedback. Let me know if you see anything else that is wrong/needs tweaking. :+1:

MiYanni commented 8 years ago

I've pushed some more changes. I've got the input colors correct now. Bitmaps in C# are stored in BGRA order (reverse ARGB), so I've fixed it when I send it to your algorithm so it is in the correct RGBA order. But, I need help. I was attempting to fix the output so I could get your result for the smiley (using it as my test case). The non-deterministic factor makes testing quite difficult. But, here's the best result I've gotten so far with the code currently in the repo: image

As you can see, it is close to your result, but I can't get the actual face. Here is your result (from the JAR): image

I've tried to remove the unsigned to signed shifting, but there still is some fundamental problem. Also, Resharper is indicating there is possible fraction loss, and I can't tell if it is intended or not. In C#, when you divide an int by an int, the it truncates the fractional part of the int. To solve it, you have to make one of the components (quotient or divisor) a decimal type (float, double, decimal). Are these truncations intended? image

Edit; Actually, I just pushed code to remove the 'fraction loss' issue stated above. However, I've noticed other issues where there is an equality comparison between float/doubles, which has no guarantee to be equal in C#. You have to do a tolerance comparison for 'equality'. Should I change these to do that style of comparsion? image

jankovicsandras commented 8 years ago

1.

It's a very good idea to create an interactive GUI or other user friendly application, I had sadly no time for this yet. There's a very basic test UI in imagetracerjs: https://github.com/jankovicsandras/imagetracerjs/blob/master/imagetracer_test_automation.html

2.

I'm planning a new feature in the next update: option presets. It's trivial to serialize options as JSON with imagetracerjs, but it's a good idea to implement something similar in imagetracerjava. I'll think about this.

3.

I don't know how C# handles Bitmaps, so I can't help you, sadly. But I did a lot of trial-and-error with Java as well, so good luck!

4. The smiley missing eyes and mouth

Your output looks good, I think it's only missing the Z-indexing which causes that smiley's eyes and mouth is "under" its face now and not visible. SVG has no z-index attribute (More info about SVG rendering order), so the paths must be sorted first. They are not rendered like

for(every layer){
    for(every path) {
        path.render()
    }
}

, but first a rendering queue is made. imagetracerjs needs explicit z-index sorting, but it's a bit easier with Java, as I use a TreeMap, which sorts itself automatically. The "label" or the z-index value, on which the paths are sorted is basically the top-left start point of the path. Use a map like TreeMap in C#, which is sorted automatically, or implement explicit sorting / port from imagetracerjs.

5. The non-deterministic factor is irritating

I agree with this, so I made a guide: choices for deterministic tracing

6. Fraction loss with ratio: You found a bug. Thanks! ☺

Fixed: ratio = (float)( (double)(paletteacc[k][4]) / (double)(imgd.width*imgd.height) );

7. Fraction loss / comparing different types with segtype2

There's something like this in the code:

double segtype1, segtype2;
...
segtype2 = -1; // int literal
...
while(... 
    (path.get(seqend)[2]==segtype2)
...

where path.get(seqend)[2] is double but segtype2 is set to -1 int literal. I don't think this comparison should cause problems, but you may change it to something like

segtype2 = -1.0; // double literal

8. Issues

Should we close this Issue and reopen the different questions as separate Issues?

MiYanni commented 8 years ago

Sorry. I've been away working on another project for the last few days.

1. I'll get to this soon (creating a GUI). 2. I've just serialized the Options. I'll have a way to save/load them in the GUI. 3. I fixed the color issue. The RGBA components were in the wrong order. 4. I fixed this issue (layer ordering) using a SortedDictionary. Basically, HashMap => Dictionary, TreeMap => SortedDictionary, just FYI. 5. I'll look over the deterministic-ness guide and see what I can do in the code/GUI to make it easier to use. 6. I think I found another issue. You cast the second parameter in this call to an int when the method takes a float. Intended? batchpathscan(rawlayers,(int)(Math.floor(options.get("pathomit")))); 7. I'll be looking over these shortly. 8. Up to you.

jankovicsandras commented 8 years ago

I'm happy to hear that you could fix the issues. Pathomit is logically an integer, but I use a HashMap<String,Float> for all options because it seemed simpler than creating a separate class for them. I'll probably extend the documentation with the logically reasonable value ranges.