Jump to content

Neil Cassidy

  • Posts

  • Joined

  • Last visited

Everything posted by Neil Cassidy

  1. I mostly focused on #4, and I think it would be possible (but not easy) to turn the papers and source code into a working plug-in. You'd have to build the priors outside of Paint.NET. And note that the result would basically be an even-more-awesome version of Increase Local Contrast (and Local Contrast Enhancement).
  2. Hi, Rick. Thanks for writing. Just wanted to clear up a misconception regarding code segment 1: "However, when the work items first start out it's possible that the computation will be performed up to N times, where N is the # of threads being used." If the flag is not set, as many as N threads may queue up to enter the monitor. They will enter in turn. The first thread to enter the monitor will always compute the result and clear the flag before leaving *. After this, any threads that enter the monitor will immediately exit without performing the computation, because the flag has already been cleared *. So the inner if-statement prevents the second computation, and the outer one is just an optimization to avoid unnecessary acquisition of the lock. * Obviously these statements are not true if one admits break, goto, return, computations that don't terminate, tampering with the lock or flag, probably many other things. Thanks for mentioning that the first slice is run by itself! It's quite helpful to know. In some cases I observe two threads entering MJW's critical section on my dual-core machine, which I can't seem to explain. This only occurs for wide, short regions.
  3. I wrote a test earlier, and did observe multiple entries to the critical section with certain selections (specifically short and wide ones). You could just modify the plug-in to lock the critical section and thereby avoid both problems. I can update this post with the modifications if you want, it was a four-line change. Might be helpful to phrebh too, because your code is perfect if we assume there's only one thread.
  4. No problem MJW! I can tell you what will happen if it's moved to the end: the race will always finish in the correct order, with the first call to Render computing the average before any other calls reach the output step, and the artifacts will never appear. But multiple threads will always enter the critical section, resulting in unnecessary computation. By "always", I really mean "almost always". Even when the flag-clearing line is the first or last line in the critical section, there's technically still some tiny chance of another thread becoming active at the wrong time, which can lead to unnecessary computation or a race (respectively). I mean it's not like anyone's writing Paint.NET plug-ins that fly airplanes or anything. But making sure that everything is thread-safe is a good habit. I hope all this is useful to you phrebh... it's definitely related to the problems you're having!
  5. Hi MJW, good question. I thought of a few possibilities that might explain why you don't see errors, and decided to test them by modifying your plug-in. I tried to give other threads a chance to interrupt the first thread after it had entered the critical section and cleared the flag, by adding Thread.Sleep and Thread.SpinWait statements. Results: I conclude that it's normally OK because the average calculation in the first call is fast enough to finish before other calls start writing stuff out. The underlying race condition might be an issue for plug-ins with longer-running calculations. In particular, the calculation in phrebh's code will probably take longer, so the artifacts he's seeing may very well be attributable to this. (edit: long discussion of bug in test)
  6. Thanks for writing, MJW! Multiple processors might affect the order of serialization, but I'm quite sure the lock statement still behaves as expected with multiple cores. Rick just answered your question about the number of rendering threads. In your code for "Plugin to Average Color of Selection", if thread X enters "if (firstTime)" but doesn't manage to change "firstTime" before transfer of control occurs, then thread Y will still be free to enter the block. The difference is probably only a couple of lines of IL, so it's very unlikely to happen, and if it does occur, it just results in some unnecessary computation. This is because you're correctly using local variables to compute your result. phrebh is using an instance variable (AverageColor), and when multiple threads get into that block, they'll all be taking turns modifying it recursively, in an unpredictable order. Also, important to note that phrebh's code would still be writing AverageColor out to destination pixels before it's actually done computing it, which is probably the most serious issue. Note that this is done by-value. If you explicitly serialize the computations by using a lock, then you can use a flag to ensure that only one computation ever occurs. My code uses whether AverageColor is null or not as a flag. Most importantly, the flag and locks keep any thread from writing anything to the output until the computation is finished. But as an added bonus, the flag prevents unnecessary computation. The extra if statement is to prevent any thread from trying to acquire the lock if the average has already been computed. This allows subsequent calls to Render to run in parallel. My experience with parallel programming is limited, but I feel like this would be pretty common... maybe there's a better way to express it in C#?
  7. I know this is a month old or so, but I just came across it, pretty cool. Thanks, flip! edit: made glow thinner
  8. Hey phrebh, I know what you mean about persistence, I'd like some too. I think you've got a good idea here with this HSV average thing, I want to see how it turns out. Maybe I can help you with your code? First up, two synchronization issues... 1. Other threads may set destination pixels to AverageColor while the first thread is still in the process of updating AverageColor. There's nothing to stop them! 2. Unless the C# compiler magically infers that you want an atomic test-and-set, it's possible for two or more threads to get into the "if (AverageColor == null)" block (or the "if (!set)" block) before you assign AverageColor (or change set to true). You can keep other threads out of a block by using the "lock" statement, it's a nice little bit of syntactic sugar for entering and exiting a Monitor. Code segment removed, because it's wrong. If you can sort out the synchronization issues, we can talk about problems with the algorithm you're using to compute the average, and about getting a result for the hue channel that makes sense. I've got some ideas for you. Hope I could help!
  9. OK, I tested this more carefully. 1. When one dimension is scaled down, it seems to choose bicubic. 2. When one dimension is scaled up, it seems to choose bicubic. 3. When both dimensions are scaled down, it seems to choose supersampling. 4. When both dimensions are scaled up, it seems to choose bicubic. 5. When one dimension is scaled up and the other is scaled down, it seems to choose bicubic. So my guess doesn't make sense in cases 4 and 5. I suppose that supersampling doesn't make sense when the image is being scaled up, because the output image could be quite large and most of the supersamples would be redundant. In those cases it must do a full bicubic interpolation, or at least two cubic interpolations in succession. I'm gonna dig around in the assemblies and see if I can figure this out. Time passes... Yeah, took a look earlier. The "Best Quality" method will only use supersampling when the height of the resized image is less than that of the original image, and the width of the resized image is less than that of the original image. Otherwise it switches to bicubic. It will use nearest-neighbour if either one of the images is either one pixel wide or one pixel tall, but it won't say so in the dialog.
  10. Playing with the control is a little confusing because the method name in the method-indicator-thing seems to lag one step behind the inputs to the stepper-arrow-things. After accounting for this, I always observe that it chooses bicubic when only one dimension is being stretched/shrunk, and I always observe that it chooses supersampling when both dimensions are being stretched/shrunk. When only one dimension is being stretched/shrunk, bicubic interpolation reduces to cubic interpolation in that dimension. Relative to bicubic interpolation, cubic interpolation is easier to code and faster. So it probably uses cubic interpolation for accuracy when only one dimension is being stretched/shrunk, and defaults to supersampling when both dimensions are being stretched/shrunk because supersampling isn't really any more complex in that case. I haven't seen the source, but that's my guess.
  11. This is great dpy, thanks! It seems like you could add a real skew effect as well, thanks to the difference between "perspective" and "dpy perspective" that you mentioned above. That would be fantastic. There's already an effect that does this (called "octagonal/quad reshape/matte"), but it can be tricky to get the shape right every time. I always have to load up Paint when I need to do it.
  12. Jesse Chunn has an effect called Soften Edges: http://paintdotnet.forumer.com/viewtopic.php?f=16&t=23103
  13. Thanks! Here's a demonstration of the difference between Gaussian Blur and Eigen Blur, on a simple test image. The white circle was on its own layer, and was not anti-aliased. I applied each blur to the circle itself, using a radius of 50, and then merged each down onto a copy of the background layer. The artifacts appear because Gaussian Blur's filter isn't rotationally symmetric. (Edit: made border transparent)
  14. Hi everyone Here's my first plug-in, an improved Gaussian blur effect which uses brand new convolution code. It's called Eigen Blur, because the other names were taken! Coolest feature: When you choose the same "radius" in this effect as in the Gaussian Blur effect, the amount of smoothing is precisely the same. But it uses a true Gaussian blur, avoiding artifacts. If you're working with a very large image, you can "preview" by using the much faster Gaussian Blur effect to determine what level of smoothing you want, and then apply Eigen Blur with the same "radius" for high-quality results. Other features: [*:2dzcj734] Running the effect leaves the overall brightness of the image unchanged. [*:2dzcj734] Option to reflect or wrap at edges (called "tileable", like True Blur). [*:2dzcj734] Option to blur RGB + Alpha, RGB only, or Alpha only (like Gaussian Blur+). It's not perfect, though! It's slower than Gaussian Blur, Gaussian Blur+, and True Blur. There may be a few bugs. Please let me know if you spot anything that I could add or fix! Thanks to Rick Brewster, Tom Jackson, and BoltBait for their work on Paint.NET and Code Lab. To install, unzip EigenBlur.zip, and copy EigenBlur.dll into your Paint.NET "Effects" folder. For me, this is C:\Program Files\Paint.NET\Effects\. Source and license (MIT) are now included in the .zip file. EigenBlur.zip
  • Create New...