Jump to content

Code problem - Listing colors in image


Recommended Posts

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;
        }
    }
}

Link to comment
Share on other sites

  • 4 weeks later...

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;
            }
        }

PdnSig.png

Plugin Pack | PSFilterPdn | Content Aware Fill | G'MICPaint 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

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.

Link to comment
Share on other sites

@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). 

PdnSig.png

Plugin Pack | PSFilterPdn | Content Aware Fill | G'MICPaint 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

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.

Link to comment
Share on other sites

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 by Felix The Ghost
Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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.B);
*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;
}
Link to comment
Share on other sites

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 by Felix The Ghost
Link to comment
Share on other sites

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 by Felix The Ghost
Link to comment
Share on other sites

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 by TechnoRobbo

Go out there and be amazing. Have Fun, TR
TRsSig.png?raw=1
Some Pretty Pictures Some Cool Plugins

Link to comment
Share on other sites

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).

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

Felix,

 

I hope you don't mind but I streamlined your code and created 2 versions

  1. A single threaded version that is what you had intended, a 1 to 1 color replacement
  2. 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 by TechnoRobbo

Go out there and be amazing. Have Fun, TR
TRsSig.png?raw=1
Some Pretty Pictures Some Cool Plugins

Link to comment
Share on other sites

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 by Felix The Ghost
Link to comment
Share on other sites

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 by TechnoRobbo

Go out there and be amazing. Have Fun, TR
TRsSig.png?raw=1
Some Pretty Pictures Some Cool Plugins

Link to comment
Share on other sites

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 by Felix The Ghost
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...