Felix The Ghost Posted November 12, 2013 Share Posted November 12, 2013 Referring to your site (contains typo, by the way), "When Paint.NET is getting ready to render your effect, it breaks up the current selection into conveinent work units" -- I think this may be causing desynchronization across the units in a function I am developing. I am creating a List that all these work units read/modify realtime to index colors in an image (currently placing color for the first occurrence of a new color in the image) but sometimes it can index a few colors more than once. How might I modify this code to avoid this issue? #region UICode #endregion List<ColorBgra> colors = new List<ColorBgra>(); bool match = false; void Render(Surface dst, Surface src, Rectangle rect) { ColorBgra CurrentPixel; for (int y = rect.Top; y < rect.Bottom; y++) { for (int x = rect.Left; x < rect.Right; x++) { CurrentPixel = src[x,y]; for (int i = 0; i < colors.Count; i++) { if (CurrentPixel.Equals(colors[i])) { match = true; break; } } if (!match) //first occurrence of color { colors.Add(CurrentPixel); CurrentPixel.R = 255; //mark pixels so I can count the number of colors for debug purposes CurrentPixel.G = 0; CurrentPixel.B = 255; } dst[x,y] = CurrentPixel; match = false; } } } Quote Link to comment Share on other sites More sharing options...
Felix The Ghost Posted December 11, 2013 Author Share Posted December 11, 2013 I don't know if my post was overlooked or no one knew, but I'm still waiting for an answer if anyone is willing Quote Link to comment Share on other sites More sharing options...
null54 Posted December 11, 2013 Share Posted December 11, 2013 I don't know if my post was overlooked or no one knew, but I'm still waiting for an answer if anyone is willing try the following: List<ColorBgra> colors = new List<ColorBgra>(); bool match = false; object sync = new object(); void Render(Surface dst, Surface src, Rectangle rect) { ColorBgra CurrentPixel; for (int y = rect.Top; y < rect.Bottom; y++) { for (int x = rect.Left; x < rect.Right; x++) { lock (sync) { CurrentPixel = src[x,y]; for (int i = 0; i < colors.Count; i++) { if (CurrentPixel.Equals(colors[i])) { match = true; break; } } if (!match) //first occurrence of color { colors.Add(CurrentPixel); CurrentPixel.R = 255; //mark pixels so I can count the number of colors for debug purposes CurrentPixel.G = 0; CurrentPixel.B = 255; } dst[x,y] = CurrentPixel; match = false; } } Quote Plugin Pack | PSFilterPdn | Content Aware Fill | G'MIC | Paint Shop Pro Filetype | RAW Filetype | WebP Filetype The small increase in performance you get coding in C++ over C# is hardly enough to offset the headache of coding in the C++ language. ~BoltBait Link to comment Share on other sites More sharing options...
Ego Eram Reputo Posted December 11, 2013 Share Posted December 11, 2013 Felix: I'm going to split your posts out of the CodeLab thread. I suspect this might be why you've not got a response I'll dump these into their own thread and see if we can scare up a few more responses. Quote ebook: Mastering Paint.NET | resources: Plugin Index | Stereogram Tut | proud supporter of Codelab plugins: EER's Plugin Pack | Planetoid | StickMan | WhichSymbol+ | Dr Scott's Markup Renderer | CSV Filetype | dwarf horde plugins: Plugin Browser | ShapeMaker Link to comment Share on other sites More sharing options...
Felix The Ghost Posted December 11, 2013 Author Share Posted December 11, 2013 (edited) @Ego Eram Reputo Thank you @null54, could you explain what is happening in that code? It may work like a charm, but I'm not a fan of just using someone else's code without understanding it. Thanks Edited December 11, 2013 by Felix The Ghost Quote Link to comment Share on other sites More sharing options...
null54 Posted December 11, 2013 Share Posted December 11, 2013 @null54, could you explain what is happening in that code? It may work like a charm, but I'm not a fan of just using someone else's code without understanding it. Thanks The lock statement prevents multiple threads from trying to update the list at the same time (See lock Statement (C# Reference) for more information). Quote Plugin Pack | PSFilterPdn | Content Aware Fill | G'MIC | Paint Shop Pro Filetype | RAW Filetype | WebP Filetype The small increase in performance you get coding in C++ over C# is hardly enough to offset the headache of coding in the C++ language. ~BoltBait Link to comment Share on other sites More sharing options...
Felix The Ghost Posted December 12, 2013 Author Share Posted December 12, 2013 In theory that sounds awesome, but I can't tell if I'd be losing the benefits of multi-threading here (I guess I don't know exactly how it works though I read the article) I guess I will have to just try it when I can. Quote Link to comment Share on other sites More sharing options...
Rick Brewster Posted December 12, 2013 Share Posted December 12, 2013 You probably shouldn't use a List for this. Instead, use a HashSet. Quote The Paint.NET Blog: https://blog.getpaint.net/ Donations are always appreciated! https://www.getpaint.net/donate.html Link to comment Share on other sites More sharing options...
Felix The Ghost Posted December 12, 2013 Author Share Posted December 12, 2013 I'm eventually going to have an identical size List to do a pseudo "palette swap" effect so I think I will have to preserve the order of the elements. I'll also not use it on images of many colors so speed shouldn't be an issue. Quote Link to comment Share on other sites More sharing options...
Ego Eram Reputo Posted December 12, 2013 Share Posted December 12, 2013 I can forsee issues ensuring both lists/palettes are the same size. Hashset is restricted to unique set of entries - it might be worth a look to avoid color duplication(s) you posted about earlier. Quote ebook: Mastering Paint.NET | resources: Plugin Index | Stereogram Tut | proud supporter of Codelab plugins: EER's Plugin Pack | Planetoid | StickMan | WhichSymbol+ | Dr Scott's Markup Renderer | CSV Filetype | dwarf horde plugins: Plugin Browser | ShapeMaker Link to comment Share on other sites More sharing options...
Felix The Ghost Posted December 13, 2013 Author Share Posted December 13, 2013 (edited) This is what I have so far: #region UICode int Amount1=3; //[0,255]Hue Variation #endregion List<ColorBgra> colors = new List<ColorBgra>(); List<ColorBgra> off_colors = new List<ColorBgra>(); HsvColor hsv = new HsvColor(0,0,0); int hue_plus = 0; int hue_minus = 0; bool match = false; object sync = new object(); Random rnd = new Random(); void Render(Surface dst, Surface src, Rectangle rect) { ColorBgra CurrentPixel; for (int y = rect.Top; y < rect.Bottom; y++) { for (int x = rect.Left; x < rect.Right; x++) { lock (sync) { CurrentPixel = src[x,y]; for (int i = 0; i < colors.Count; i++) { if (CurrentPixel.Equals(colors[i])) { match = true; CurrentPixel = off_colors[i]; break; } } if (!match) { colors.Add(CurrentPixel); hsv = HsvColor.FromColor(CurrentPixel); hue_plus = rnd.Next((Amount1/2)); hue_minus = rnd.Next((Amount1/2)); hsv.Hue += hue_plus; hsv.Hue -= hue_minus; off_colors.Add(ColorBgra.FromColor(hsv.ToColor())); CurrentPixel = ColorBgra.FromColor(hsv.ToColor()); } dst[x,y] = CurrentPixel; match = false; } } } } Both Lists are increased in size at the same step so they should stay the same size. Lots of redundant code in the (!match) block because I guess I don't understand the ColorBgra/HsvColor data types; What was supposed to be a simple += turned into that experimental mess (still doesn't work -- guessing this kind of behavior is not allowed and the struct needs to be recreated each time instead of modifying its members directly?) Not sure where to read up on the datatypes Paint.net uses. I couldn't even tell you the data types of their members though I found out r,g,b,a are bytes. The goal is to be able to "tweak" colors in an image randomly to sort of mutate placeholder colors into something more desirable. Edited December 13, 2013 by Felix The Ghost Quote Link to comment Share on other sites More sharing options...
Felix The Ghost Posted December 15, 2013 Author Share Posted December 15, 2013 Where can I learn how to use ColorBgra/HsvColor? Quote Link to comment Share on other sites More sharing options...
BoltBait Posted December 15, 2013 Share Posted December 15, 2013 One of my tutorials has a section on it. http://boltbait.com/pdn/CodeLab/help/tutorial4.php Hope this helps. Quote Click to play: Download: BoltBait's Plugin Pack | CodeLab | and how about a Computer Dominos Game Link to comment Share on other sites More sharing options...
Felix The Ghost Posted December 15, 2013 Author Share Posted December 15, 2013 One of my tutorials has a section on it. http://boltbait.com/pdn/CodeLab/help/tutorial4.asp Hope this helps. Thanks, I'm aware of that tutorial, but I'm surprised there isn't something somewhere that's more in-depth (Even just the source code for the data types would help) I don't even know the functional ranges of H/S/B. (like R/G/B is 0-255) The tutorial does confirm my suspicions on the need to reassemble the structure each time a member is modified, though. Quote Link to comment Share on other sites More sharing options...
BoltBait Posted December 15, 2013 Share Posted December 15, 2013 While I did use a slightly different HSV to RGB routine, I did use HSV colors for my Pastel plugin. Source code is around here somewhere... EDIT: http://forums.getpaint.net/index.php?/topic/2045-pastelwater-color-plugin-updated-2008-02-18/ Now, let's see if I can find the source code... EDITEDIT: // Title: BoltBait's Pastel Effect v3.5 // Author: BoltBait // Name: Pastel // Submenu: Artistic // URL: http://www.boltbait.com/pdn #region UICode int Amount1 = 3; // [1,8] Pastel Size int Amount2 = 50; // [3,255] Roughness #endregion private OilPaintingEffect oilPaintEffect = new OilPaintingEffect(); private PropertyCollection oilPaintProps; unsafe void Render(Surface dst, Surface src, Rectangle rect) { double H = 0, S = 0, V = 0; double BaseColors = 36; // 1-360, step 1, Default = 72 double Lighter = 1.0; // 1-2, step .1, Default = 1.0 double Darker = 30; // 1-100, step 1, Default = 30 double Q = 360 / BaseColors; // Oil Paint backgound this.oilPaintProps = this.oilPaintEffect.CreatePropertyCollection(); PropertyBasedEffectConfigToken oilPaintToken = new PropertyBasedEffectConfigToken(this.oilPaintProps); oilPaintToken.SetPropertyValue(OilPaintingEffect.PropertyNames.BrushSize, Amount1); oilPaintToken.SetPropertyValue(OilPaintingEffect.PropertyNames.Coarseness, Amount2); this.oilPaintEffect.SetRenderInfo(oilPaintToken, new RenderArgs(dst), new RenderArgs(src)); this.oilPaintEffect.Render(new Rectangle[1] { rect }, 0, 1); for(int y = rect.Top; y < rect.Bottom; y++) { ColorBgra* dstPtr = dst.GetPointAddressUnchecked(rect.Left, y); for (int x = rect.Left; x < rect.Right; x++) { ColorBgra CurrentPixel = *dstPtr; EvanRGBtoHSV(CurrentPixel.R, CurrentPixel.G, CurrentPixel.B, ref H, ref S, ref V); H = Q * (double)Math.Round(H / Q); S /= Lighter; V *= 100; V = V / (Darker * 10); V += (1.0 - (Darker / 100)) + 0.1; if (V > 1) V = 1; EvanHSVtoRGB(H, S, V, ref CurrentPixel.R, ref CurrentPixel.G, ref CurrentPixel.; *dstPtr = CurrentPixel; dstPtr++; } } } private void EvanHSVtoRGB(double H, double S, double V, ref byte bR, ref byte bG, ref byte bB) { const double HSV_UNDEFINED = -999.0; // Parameters must satisfy the following ranges: // 0.0 <= H < 360.0 // 0.0 <= S <= 1.0 // 0.0 <= V <= 1.0 // Handle special case first if (S == 0.0 || H == HSV_UNDEFINED) { byte x = (byte)(int)(V * 255.0); bR = x; bG = x; bB = x; return; } if (H >= 360.0) { H = AngleConstrain(H); } double R = V, G = V, B = V; double Hi = Math.Floor(H / 60.0); double f = H / 60.0 - Hi; double p = V * (1.0 - S); double q = V * (1.0 - f * S); double t = V * (1.0 - (1.0 - f) * S); if (Hi == 0.0) { R = V; G = t; B = p; } else if (Hi == 1.0) { R = q; G = V; B = p; } else if (Hi == 2.0) { R = p; G = V; B = t; } else if (Hi == 3.0) { R = p; G = q; B = V; } else if (Hi == 4.0) { R = t; G = p; B = V; } else if (Hi == 5.0) { R = V; G = p; B = q; } int iR = (int)(R * 255.0); int iG = (int)(G * 255.0); int iB = (int)(B * 255.0); bR = (byte)iR; bG = (byte)iG; bB = (byte)iB; } private void EvanRGBtoHSV(int R, int G, int B, ref double outH, ref double outS, ref double outV) { const double HSV_UNDEFINED = -999.0; // R, G, and B must range from 0 to 255 // Ouput value ranges: // outH - 0.0 to 360.0 // outS - 0.0 to 1.0 // outV - 0.0 to 1.0 double dR = (double)R / 255.0; double dG = (double)G / 255.0; double dB = (double)B / 255.0; double dmaxRGB = EvanMax3(dR, dG, dB); double dminRGB = EvanMin3(dR, dG, dB); double delta = dmaxRGB - dminRGB; // Set value outV = dmaxRGB; // Handle special case if (dmaxRGB == 0) { outH = HSV_UNDEFINED; outS = 0.0; return; } outS = delta / dmaxRGB; if (dmaxRGB == dminRGB) { outH = HSV_UNDEFINED; return; } // Finally, compute hue if (dR == dmaxRGB) { outH = (dG - dB) / delta * 60.0; } else if (dG == dmaxRGB) { outH = (2.0 + (dB - dR) / delta) * 60.0; } else //if (dB == dmaxRGB) { outH = (4.0 + (dR - dG) / delta) * 60.0; } if (outH < 0) { outH += 360.0; } } private double EvanMax3(double x, double y, double z) { return (x > y) ? ((x > z) ? x : z) : ((y > z) ? y : z); } private double EvanMin3(double x, double y, double z) { return (x < y) ? ((x < z) ? x : z) : ((y < z) ? y : z); } private double AngleConstrain(double MyAngle) { // Makes sure that 0.0 <= MyAngle < 360.0 // "Wraps around" the value if it's outside this range if (MyAngle >= 360.0) { MyAngle -= Math.Floor(MyAngle / 360.0) * 360.0; } if (MyAngle < 0.0) { MyAngle += 360.0; } return MyAngle; } Quote Click to play: Download: BoltBait's Plugin Pack | CodeLab | and how about a Computer Dominos Game Link to comment Share on other sites More sharing options...
Felix The Ghost Posted December 16, 2013 Author Share Posted December 16, 2013 Thanks! Very useful. Quote Link to comment Share on other sites More sharing options...
Felix The Ghost Posted December 19, 2013 Author Share Posted December 19, 2013 (edited) Looked good at a glance, but Paint.net uses int and not double for hsv. So what is the functional range of H, S, V? Edit: Going to guess based on the color window in Paint.net: 0..360 0..100 0..100 Append: Seems to work well, but I have trouble accessing the user variables Amount1 etc within the lock() sections. It just returns the value they were initialized with Edited December 19, 2013 by Felix The Ghost Quote Link to comment Share on other sites More sharing options...
Felix The Ghost Posted December 21, 2013 Author Share Posted December 21, 2013 (edited) This is really frustrating. I keep changing code with poor version control so I'm posting this to see if anyone sees something obviously wrong that I don't. #region UICode int Amount1=1; //[0,100]Hue Adjust int Amount2=1; //[0,100]Saturation Adjust int Amount3=1; //[0,100]Value Adjust byte Amount4 = 0; // [255] Reseed #endregion List<Color> colors = new List<Color>(); List<Color> off_colors = new List<Color>(); object sync = new object(); void Render(Surface dst, Surface src, Rectangle rect) { bool match = false; ColorBgra CurrentPixel; //populate colors for (int y = rect.Top; y < rect.Bottom; y++) { for (int x = rect.Left; x < rect.Right; x++) { CurrentPixel = src[x,y]; match = false; lock (sync) { for (int i = 0; i < colors.Count; i++) { if (CurrentPixel.ToColor().Equals(colors[i])) { match = true; break; } } if (!match) { colors.Add(CurrentPixel.ToColor()); } } } } //tweak colors HsvColor hsv; int H = 0, S = 0, V = 0; for (int i = 0; i < colors.Count; i++) { //hsv = HsvColor.FromColor(colors[i]); //H = hsv.Hue; //S = hsv.Saturation; //V = hsv.Value; //H -= (RandomNumber.Next(Amount1 * 180 / 100)); //H += (RandomNumber.Next(Amount1 * 180 / 100)); //S -= (RandomNumber.Next(Amount2) / 2); //S += (RandomNumber.Next(Amount2) / 2); //V -= RandomNumber.Next(Amount3) / 2; //V += RandomNumber.Next(Amount3) / 2; //if (H < 0) H = 360; //if (H > 360) H = 360; //if (S < 0) S = 0; //if (S > 100) S = 100; //if (V < 0) V = 100; //if (V > 100) V = 100; //hsv = new HsvColor(H,S,V); //off_colors.Add(hsv.ToColor()); off_colors.Add(colors[i]); } // Colorize for (int y = rect.Top; y < rect.Bottom; y++) { for (int x = rect.Left; x < rect.Right; x++) { CurrentPixel = src[x,y]; for (int i = 0; i < colors.Count; i++) { if (CurrentPixel.ToColor().Equals(colors[i])) { CurrentPixel = ColorBgra.FromColor(off_colors[i]); break; } } dst[x,y] = CurrentPixel; } } } Edited December 21, 2013 by Felix The Ghost Quote Link to comment Share on other sites More sharing options...
TechnoRobbo Posted December 21, 2013 Share Posted December 21, 2013 (edited) Since "Render" is done little batches of ROI's (like solving a jigsaw puzzle) and not sequentially, your color table is not fully populated before you randomize and colorize. Edited December 21, 2013 by TechnoRobbo Quote Go out there and be amazing. Have Fun, TRSome Pretty Pictures Some Cool Plugins Link to comment Share on other sites More sharing options...
TechnoRobbo Posted December 22, 2013 Share Posted December 22, 2013 (edited) I checked out your code - your not clearing your lists you just keep adding to them.Also your sliders are set at minimum so the first render doesn't show an obvious change. Then when you move your sliders , since the list weren't cleared , you got the same results as the first time. Hope all this helps. I got it to work by single threading but it took 5 minutes for a 320X240 Image. It does work good though. Here's the mod I used to test: Reseed after moving sliders to render. #region UICode int Amount1 = 25; // [0,100] Hue Adjust int Amount2 = 10; // [0,100] Saturation Adjust int Amount3 = 10; // [0,100] Value Adjust byte Amount4 = 0; // [255] Reseed #endregion List<Color> colors = new List<Color>(); List<Color> off_colors = new List<Color>(); object sync = new object(); byte amount4 = 255; void Render(Surface dst, Surface src, Rectangle rect) { if (Amount4!=amount4){ Random rndm = new Random((int)Guid.NewGuid().GetHashCode()); amount4=Amount4; rect = src.Bounds; bool match = false; ColorBgra CurrentPixel; //populate colors colors.Clear(); for (int y = rect.Top; y < rect.Bottom; y++) { for (int x = rect.Left; x < rect.Right; x++) { CurrentPixel = src[x,y]; match = false; for (int i = 0; i < colors.Count; i++) { if (CurrentPixel.ToColor().Equals(colors[i])) { match = true; break; } } if (!match) { colors.Add(CurrentPixel.ToColor()); } } } //tweak colors HsvColor hsv; int H = 0, S = 0, V = 0; off_colors.Clear(); for (int i = 0; i < colors.Count; i++) { hsv = HsvColor.FromColor(colors[i]); H = hsv.Hue; S = hsv.Saturation; V = hsv.Value; H -= rndm.Next(Amount1 * 180 / 100); H += rndm.Next(Amount1 * 180 / 100); S -= rndm.Next(Amount2) / 2; S += rndm.Next(Amount2) / 2; V -= rndm.Next(Amount3) / 2; V += rndm.Next(Amount3) / 2; if (H < 0) H = 0; if (H > 360) H = 360; if (S < 0) S = 0; if (S > 100) S = 100; if (V < 0) V = 0; if (V > 100) V = 100; hsv = new HsvColor(H,S,V); off_colors.Add(hsv.ToColor()); //off_colors.Add(colors[i]); } // Colorize for (int y = rect.Top; y < rect.Bottom; y++) { for (int x = rect.Left; x < rect.Right; x++) { CurrentPixel = src[x,y]; for (int i = 0; i < colors.Count; i++) { if (CurrentPixel.ToColor().Equals(colors[i])) { CurrentPixel = ColorBgra.FromColor(off_colors[i]); break; } } dst[x,y] = CurrentPixel; } } } } Edited December 22, 2013 by TechnoRobbo Quote Go out there and be amazing. Have Fun, TRSome Pretty Pictures Some Cool Plugins Link to comment Share on other sites More sharing options...
Rick Brewster Posted December 22, 2013 Share Posted December 22, 2013 You're also using List<T> without any of your own synchronization. It is not a thread-safe class, and thus your use of it is effectively undefined. I highly recommend using ConcurrentSet instead (it's in the PaintDotNet.Collections namespace in paint.net 4.0). Quote The Paint.NET Blog: https://blog.getpaint.net/ Donations are always appreciated! https://www.getpaint.net/donate.html Link to comment Share on other sites More sharing options...
TechnoRobbo Posted December 22, 2013 Share Posted December 22, 2013 (edited) Felix, I hope you don't mind but I streamlined your code and created 2 versions A single threaded version that is what you had intended, a 1 to 1 color replacement a multi threaded version that replaces colors dependent on the ROI's that have been processed The streamlined single threaded can process a 320X240 image in 2 minutes on my HP laptop The Multi threaded can process a 1024X768 in 1:40 The single threaded requires reseed to be pressed to render slider changes the multi threaded does not They both give pleasing effects but I do believe the single threaded is what you have in mind. Both are in this zip file felixMultiandSingle.zip SingleThreaded Code Hidden Content: #region UICode int Amount1 = 25; // [0,100] Hue Adjust int Amount2 = 10; // [0,100] Saturation Adjust int Amount3 = 10; // [0,100] Value Adjust byte Amount4 = 0; // [255] Reseed #endregion object sync = new object(); byte amount4 = 255; void Render(Surface dst, Surface src, Rectangle rect) { if (Amount4!=amount4){ Random rndm = new Random((int)Guid.NewGuid().GetHashCode()); amount4=Amount4; rect = src.Bounds; bool match = false; int colorIdx = 0; Color[] colors = new Color[rect.Width * rect.Height]; Color[] off_colors = new Color[rect.Width * rect.Height]; ColorBgra CurrentPixel; //populate colors for (int y = rect.Top; y < rect.Bottom; y++) { for (int x = rect.Left; x < rect.Right; x++) { CurrentPixel = src[x,y]; bool nomatch=true; for (int i = 0; i < colorIdx; i++) { if (CurrentPixel.ToColor().Equals(colors[i])){ nomatch=false; dst[x,y]= ColorBgra.FromColor(off_colors[i]); break; } } if (nomatch){ colors[colorIdx] = CurrentPixel.ToColor(); HsvColor hsv; int H = 0, S = 0, V = 0; hsv = HsvColor.FromColor(colors[colorIdx]); H = hsv.Hue; S = hsv.Saturation; V = hsv.Value; H -= rndm.Next(Amount1 * 180 / 100); H += rndm.Next(Amount1 * 180 / 100); S -= rndm.Next(Amount2) / 2; S += rndm.Next(Amount2) / 2; V -= rndm.Next(Amount3) / 2; V += rndm.Next(Amount3) / 2; if (H < 0) H = 0; if (H > 360) H = 360; if (S < 0) S = 0; if (S > 100) S = 100; if (V < 0) V = 0; if (V > 100) V = 100; hsv = new HsvColor(H,S,V); off_colors[colorIdx]=hsv.ToColor(); dst[x,y]= ColorBgra.FromColor(off_colors[colorIdx]); colorIdx++; } } } } } Multi Threaded Code Hidden Content: #region UICode int Amount1 = 25; // [0,100] Hue Adjust int Amount2 = 10; // [0,100] Saturation Adjust int Amount3 = 10; // [0,100] Value Adjust byte Amount4 = 0; // [255] Reseed #endregion object sync = new object(); void Render(Surface dst, Surface src, Rectangle rect) { Random rndm = new Random((int)Guid.NewGuid().GetHashCode()); bool match = false; int colorIdx = 0; Color[] colors = new Color[rect.Width * rect.Height]; Color[] off_colors = new Color[rect.Width * rect.Height]; ColorBgra CurrentPixel; //populate colors for (int y = rect.Top; y < rect.Bottom; y++) { for (int x = rect.Left; x < rect.Right; x++) { CurrentPixel = src[x,y]; bool nomatch=true; for (int i = 0; i < colorIdx; i++) { if (CurrentPixel.ToColor().Equals(colors[i])){ nomatch=false; dst[x,y]= ColorBgra.FromColor(off_colors[i]); break; } } if (nomatch){ colors[colorIdx] = CurrentPixel.ToColor(); HsvColor hsv; int H = 0, S = 0, V = 0; hsv = HsvColor.FromColor(colors[colorIdx]); H = hsv.Hue; S = hsv.Saturation; V = hsv.Value; H -= rndm.Next(Amount1 * 180 / 100); H += rndm.Next(Amount1 * 180 / 100); S -= rndm.Next(Amount2) / 2; S += rndm.Next(Amount2) / 2; V -= rndm.Next(Amount3) / 2; V += rndm.Next(Amount3) / 2; if (H < 0) H = 0; if (H > 360) H = 360; if (S < 0) S = 0; if (S > 100) S = 100; if (V < 0) V = 0; if (V > 100) V = 100; hsv = new HsvColor(H,S,V); off_colors[colorIdx]=hsv.ToColor(); dst[x,y]= ColorBgra.FromColor(off_colors[colorIdx]); colorIdx++; } } } } Edited December 22, 2013 by TechnoRobbo Quote Go out there and be amazing. Have Fun, TRSome Pretty Pictures Some Cool Plugins Link to comment Share on other sites More sharing options...
Felix The Ghost Posted December 22, 2013 Author Share Posted December 22, 2013 (edited) Will check it out when I can! Thanks for the help so far. Append: I think it is important to note that it is intended to be used on images with few colors like these (not my work, just illustrates my point) Large image, but few colors, renders in less than two seconds for me. and Less than one second on this one Maybe it is possible to build in a safety when there are over say 512 unique colors (or maybe a slider) to not render if it will be slow and take a long time to cancel. And you are right the multithreaded one has a different effect than I am aiming for. I am basically mutating colors into other colors randomly with a randomly generated "palette swap" so every instance of a specific color is changed the same way. I do not understand this code: Random rndm = new Random((int)Guid.NewGuid().GetHashCode()); I thought RandomNumber was specifically created to be the best option for Code Lab? I also am not sure but wouldn't this Color[] colors = new Color[rect.Width * rect.Height]; Color[] off_colors = new Color[rect.Width * rect.Height]; negatively affect performance? It seems to assume each pixel of the image is a unique color rather than expanding in size as new unique colors are identified. I am not familiar with all the C# container classes but I do think the container should have a dynamic size (I do not know how that affects threading) Perhaps we can use ConcurrentSet as Rick recommends? This is what I am using to count unique colors in the image some images on google from older games that would be great examples have JPEG compression and you can see through the plugin it gives them many unique colors which slow down the effect. The posterize effect can help reduce color. Also, I noticed the effect ignores selections except in the preview and transparency is lost. I made a filter using CodeLab before and it respects selections, so I do not know what is wrong. I'll have to investigate further tomorrow but I wanted to give feedback immediately as I appreciate your help. Edited December 22, 2013 by Felix The Ghost Quote Link to comment Share on other sites More sharing options...
TechnoRobbo Posted December 22, 2013 Share Posted December 22, 2013 (edited) Random rndm = new Random((int)Guid.NewGuid().GetHashCode()); is a method of creating a random seed for the psuedo-random generator . I think you'll find programmers on this forum all have their favorite version. I also am not sure but wouldn't this Color[] colors = new Color[rect.Width * rect.Height]; Color[] off_colors = new Color[rect.Width * rect.Height]; negatively affect performance? Not in the single threaded version it only gets done once a render. In the multi-threaded - yes it would - good catch. I't worth testing to see how much. Post -edited: Changing the variable scope from procedure to module had no appreciable affect on performance. 1024X768 rendered at 1 minute 40 seconds like before Edited December 22, 2013 by TechnoRobbo Quote Go out there and be amazing. Have Fun, TRSome Pretty Pictures Some Cool Plugins Link to comment Share on other sites More sharing options...
Felix The Ghost Posted December 22, 2013 Author Share Posted December 22, 2013 (edited) 1024X768 rendered at 1 minute 40 seconds like before I don't mean to sound repetitive but there isn't enough information there to judge speed. I can create a 1024x768 blank image, draw a gradient and posterize it to reduce colors (reduced to 43 colors in my test) and the plugin renders in less than three seconds. Since each pixel loops through colors[] the amount of unique colors greatly affects speed I will have to see if there is some c# container that retains order but has quick-access (keyword or something) Rick, does ConcurrentSet preserve order? There needs to be a copy of the container with tweaked colors. Maybe it can be a concurrentSet with a custom struct of two colors so order is no longer important? You should test on those linked images if you haven't yet (you can drag and drop into Paint.net) Edited December 22, 2013 by Felix The Ghost Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.