Jump to content
How to Install Plugins ×

Floyd-Steinberg dithering plugin


asura

Recommended Posts

FloydSteinbergExample.png

 

Simple plugin, no options, just run it on an image or a selection. You might want to adjust contrast or levels before running this plugin for better results. The example above was dithered without any prior adjustments.

 

Copy the dll into the effects folder to install it.

Source:

<no longer available - EER>

 

Download

 

Edited by toe_head2001
Restored image
  • Like 1
Link to comment
Share on other sites

From looking at the source code, you are calling Clone() on the input Surface every time Render() is called. Your memory usage is going to explode, causing Paint.NET to crash a lot on anything but small images.

The Paint.NET Blog: https://blog.getpaint.net/

Donations are always appreciated! https://www.getpaint.net/donate.html

forumSig_bmwE60.jpg

Link to comment
Share on other sites

:oops: I had some troubles getting my code to run properly in CodeLab. The problem was that the dithering needs to apply changes to the source surface, and in CodeLab it seemed to apply the effect twice. By cloning it I was able to get around that problem, but I forgot to dispose the copy afterwards. Or can I just apply the changes to the source surface without disturbing any code outside my plugin?

Regards, Asura

Link to comment
Share on other sites

I've uploaded the corrected code and dll. It's still cloning, but now it's cleaning up after itself as well.

So on my quad-core CPU, it's still going to require 4x the memory as the original image to run your effect :( (one active rendering thread per logical CPU, with a minimum of 2 threads)

For a 7 megapixel image that means it'd require an additional 112 MB of memory ........ are you sure this requires writing to the source? I have this same algorithm implemented for saving GIF images and it just requires two passes of analysis, but doesn't require anything like this.

The Paint.NET Blog: https://blog.getpaint.net/

Donations are always appreciated! https://www.getpaint.net/donate.html

forumSig_bmwE60.jpg

Link to comment
Share on other sites

I don't have any experience with more than one core at a time. I always assumed that the cores would work together by sharing at least some the memory. Oh well, learning moment...

Anyway, I've kicked pretty much everything out and started again.

It's now only using its source and destination surface, assisted by a much smaller buffer, just as big as the width of the image. Memory impact will be much lower. I've tested this with a 4000x5400 image: Loading the image increased memory usage by 176MB. Starting the effect made it increase by another 92MB (which it does with any effect, not just this one). During processing memory usage grew with about 5 or 6MB and it dropped back when it was done.

I found that my original code was rather slow on larger images, so I also made the processing loop more efficient using pointers instead of the surface indexers. On the test image it took about 11 seconds. The previous version would've taken a few minutes probably...

And since I was busy anyway, I also documented the code :)

The dll and source are available at the original locations.

Link to comment
Share on other sites

Ahh yes, this code looks much better now. The memory you are allocating will be insignificant. If you want to optimize performance further, I of course have tons of ideas. But I think you're probably already at the break-even point where any further optimization will be a lot of work and the gains will be proportionally much smaller than what you just gained. (well maybe you could gain, say, another 50% ... but 50% of 11 seconds isn't worth as much as the 80% [or whatever] of "a few minutes" you already got)

The Paint.NET Blog: https://blog.getpaint.net/

Donations are always appreciated! https://www.getpaint.net/donate.html

forumSig_bmwE60.jpg

Link to comment
Share on other sites

Thanks for your constructive input :D I was already happy that it was faster than the 'Edge Detect' filter, which I think is a technically similar effect.

Where do you think I could gain a bit, 50% still sounds like a lot. I know it isn't, but you've got my curiosity triggered. Pointers for navigating through the cache as well, or something like that? Or throwing in some threads for the rois that need to be rendered?

Link to comment
Share on other sites

Well you're allocating at the start of every call to Render(). From a performance perspective it's better to have a static [ThreadStatic] variable for your cache variable, load it once into a method variable at the start of the Render() method (retrieving a static [ThreadStatic] var is expensive), make it bigger if necessary (if rect.Width > cache.Width { cache = new Cache(rect.Width) }). That will keep the cache in the CPU cache better, and reduce a lot of pressure on the GC (garbage collector).

The Paint.NET Blog: https://blog.getpaint.net/

Donations are always appreciated! https://www.getpaint.net/donate.html

forumSig_bmwE60.jpg

Link to comment
Share on other sites

Hmm, I've tried the static cache, but then it isn't 'clean' when the Render method is called. The image still renders in a recognizable way, but it gets polluted with noise from previous renderings. Cleaning the cache by resetting all its elements at the start of the Render method pretty much cancels out the static cache gain.

However, when I change the ColorError class to a struct it gets much faster even when I'm creating new caches every time the Render method is called. The structs are created much faster than classes, and they're instantiated automatically when I create an array. Since the default value happens to be exactly what I need, I don't need to loop through the array afterwards. According to my profiler the Floyd-Steinberg calculations are also twice as fast.

I did some performance tests with arrays of structs and arrays of classes , and the structs turned out to be much faster then the classes, with half the number of GC collections as well. An array with just one struct isn't much faster than an array with one class, but it gets better with bigger arrays. With 10000 elements it's 4 times faster, with 500000 elements it's 80x faster.

So, that sounds like a gain. I'll upload a new version later today.

Link to comment
Share on other sites

That's weird. The performance tests I did on the separate parts promised some gain, but when I put the whole thing together it actually works a little slower with my test image. Memory usage is lower, but the number of GC collections stays the same.

I guess there are some other forces at work...

Since it doesn't improve at all I've kept the code as it was. So no new upload, sorry.

Link to comment
Share on other sites

  • 7 years later...

Download link is broken here is a link to download it:

 

 

Read This Post   Only download plugins from this forum. If the link is broken don't link to outside sources. ;)

Edited by mottoman

HFuQlot.png

 

 

 

Link to comment
Share on other sites

I don't have a copy of this DLL to attach.  If anyone can provide a copy - please PM me.

Link to comment
Share on other sites

I don't have a copy of this DLL to attach.  If anyone can provide a copy - please PM me.

 

Not one from Megalo, of course.

 

The Doctor: There was a goblin, or a trickster, or a warrior... A nameless, terrible thing, soaked in the blood of a billion galaxies. The most feared being in all the cosmos. And nothing could stop it, or hold it, or reason with it. One day it would just drop out of the sky and tear down your world.
Amy: But how did it end up in there?
The Doctor: You know fairy tales. A good wizard tricked it.
River Song: I hate good wizards in fairy tales; they always turn out to be him.

Link to comment
Share on other sites

  • 1 year later...

DLL attached to the first post.

Alternatively - there is this implementation by @BoltBait Floyd-Steinberg Dithering

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...