Reptillian

A quick question: Getting the amount of non-zero alpha pixels per row or column

Recommended Posts

I have decided to attempt to convert another one of my gmic-qt filter to a pdn plugin, and part of it is converting non-zero alpha pixels count per column or row. That means a vector of size that is dependent one of the dimension within the image, and each number correspond to the number of non-alpha pixels.

 

For clarification on what I'd like to convert, if needed, this is the code I'm translating to c# -

 

 

#@cli rep_sptbwgp: (eq. to rep_shift_pixel_to_boundary_with_group_pixels)
rep_sptbwgp: rep_shift_pixel_to_boundary_with_group_pixels $*
#@cli rep_shift_pixel_to_boundary_with_group_pixels: -1<=_position<=1, _axis={ 0=Horizontal | 1=Vertical }, 0<=_influence_factor<=1, _threshold>=0,_channel_mode
#@cli : Shifts pixels to boundary by percentage using grouped pixels. position less than .5 means shift will be negative, and position greater than .5 means shift will be positive.
#@cli : Assign an argument to _channel_mode if you want to disable restricting effect on alpha channel or avoid manipulating 4 channels image in case of CMYKA image. Only use in rare cases!
#@cli : Default values: '_position=-1','_axis=1','_influence_factor=1','_threshold=0','_channel_mode='
rep_shift_pixel_to_boundary_with_group_pixels:
skip ${1=-1},${2=1},${3=.5},${4=0},${5=}

 

any_channels=0
tcr=3
influence_factor={min(abs($3),1)}
position={(($1+1)/2)}

 

if   $position<0 position=0
elif $position>1 position=1
fi

 

if narg($5)
    if $5>=0
        tcr+=1
    else
        any_channels=1
    fi
fi

 

if $influence_factor #Apply effect is non-zero alpha#
    repeat $! l[$>]
        if (s==2||s>$tcr)||$any_channels
        
            ss={s-1}
            sh $ss f. "i>$4?i:0" rm. #If alpha is less than _threshold, then it converts to 0#
            {w},{h},1,1,i(#-1,x,y,0,$ss)?1 #Create a new surface where all non-zero alpha is 1. Otherwise, 0. Used for sum later.#
            
            if $2
                {w},1,1,1,sum(crop(#-1,x,0,0,0,1,h#-1,1,1)) #Create a strip of vector containing number of non-zero alpha within row or column#
                {w#-2},{h#-2},1,{$ss+1},if(i(#-1,x,0,0,0)==h#0,i(#0)) #Copy all row or column where the number of non-zero alpha is equal to the respective dimension#
            else  
                1,{h},1,1,sum(crop(#-1,0,y,0,0,w#-1,1,1,1)) #Create a strip of vector containing number of non-zero alpha within row or column#
                {w#-2},{h#-2},1,{$ss+1},if(i(#-1,0,y,0,0)==w#0,i(#0)) #Copy all row or column where the number of non-zero alpha is equal to the respective dimension#
            fi

 

            eval ${-math_lib}"            
                influence_factor="$influence_factor";
                invert_factor=1-influence_factor;
                position="$position";
                
                if($2,
                    for(xx=0,xx<w#-1,xx++,
                        nyy=h#-1-1;
                        if(i(#-2,xx,0)&&i(#-2,xx,0)<h#-1,
                            N=i(#-2,xx,0);
                            tyy=h#-1-N;
                            nyy-=tyy*position;
                            for(yy=h#-1-1,yy>-1,yy--,
                                if(N,
                                    if(i(#-3,xx,yy),
                                        N--;
                                        tnyy=influence_factor*nyy+invert_factor*yy;
                                        if(position==1,I(#-1,xx,ceil(tnyy))=I(#-4,xx,yy);
                                                      ,I(#-1,xx,floor(tnyy))=I(#-4,xx,yy);
                                        );
                                        nyy--;
                                    );
                                    ,break();
                                );
                            );
                        );
                    );
                ,
                    for(yy=0,yy<h#-1,yy++,
                        nxx=0;
                        if(i(#-2,0,yy)&&i(#-2,0,yy)<w#-1,
                            N=i(#-2,0,yy);
                            txx=w#-1-N;
                            nxx+=txx*position;
                            for(xx=0,xx<w#-1,xx++,
                                if(N,
                                    if(i(#-3,xx,yy),
                                        N--;
                                        tnxx=influence_factor*nxx+invert_factor*xx;
                                        if(position==1,I(#-1,floor(tnxx),yy)=I(#-4,xx,yy);
                                                      ,I(#-1,ceil(tnxx),yy)=I(#-4,xx,yy);
                                        );
                                        nxx++;
                                    );
                                    ,break();
                                );
                            );
                        );
                    );
                );
            "
            k.
        fi
    endl done
fi

Edited by Reptillian

Share this post


Link to post
Share on other sites

If I correctly understand the question (which I may not), you would just count and save the number of pixels with non-zero alphas in each row or column in the pre-processing step, which is PreRender() in Codelab and OnSetRenderInfo() in Visual Studio. Since it doesn't change, a flag (likely just the non-null pointer to the array where the results are saved) should be used to prevent re-doing it each time a control is changed.

Share this post


Link to post
Share on other sites
5 minutes ago, MJW said:

If I correctly understand the question (which I may not), you would just count and save the number of pixels with non-zero alphas in each row or column in the pre-processing step, which is PreRender() in Codelab and OnSetRenderInfo() in Visual Studio. Since it doesn't change, a flag (likely just the non-null pointer to the array where the results are saved) should be used to prevent re-doing it each time a control is changed.

 

Most of that I get, but I would like the fastest way to create a vector of non-zero alpha counts. I suppose there's either do while or for loop method.

 

You lost me at flag though. Explain.

Share this post


Link to post
Share on other sites

It probably makes no significant difference whether a while or a for loop is used, especially when the optimizer is on for the Release version. And if it does make a difference, about the only way to know is to look at the compiled code, or run a profiler. In any case, it will be so quick it will almost certainly not matter. (I'd use a for loop.)

 

As to the flag:

 

OnSetRenderInfo() runs each time a control is changed, as does PreRender() (which is called from OnSetRenderInfo()). That's good, because it allows global things to be updated to take into account the new control values. However, the number of pixels in a row with non-zero alphas depends only on the state of the image at the time you run the plugin, so the count only needs to be done once. The usual way to accomplish this is to use a "first time" flag to say whether it's already been done. Since you probably need to store the results in an array, the pointer to the array is a very convenient flag. Something like this:

 

int[] rowNonZeroAlphas = null;

void PreRender(Surface dst, Surface src)
{
	int width = src.Width, height = src.Height;
	if (rowNonZeroAlphas == null)
	{
		rowNonZeroAlphas = new int[height];
		for (int y = 0; y < height; y++)
		{
			int nonZeroAlphaCount = 0;	
			for (int x = 0; x < width; x++)
			{
				if (src[x, y].Alpha != 0)
					nonZeroAlphaCount++;
			}
			rowNonZeroAlphas[y] = nonZeroAlphaCount
		}
	}
}
		

(Please forgive any mistakes)

 

The first time it runs, the array address will be null, so it will allocate the array and compute the results. On all subsequent times it's run, the address will be non-null, so it will skip over the computations.

 

  • Upvote 1

Share this post


Link to post
Share on other sites

Ok, it seems that's all I needed to do. Thanks, I'll get to converting 'Grouped Pixel Axis-Based Shift' to a PDN plugin at some point during the next weekend. It doesn't look that hard to do.

Share this post


Link to post
Share on other sites

If you need the info for both the rows and columns, the simplest way is probably:

		for (int y = 0; y < height; y++)
		{
			for (int x = 0; x < width; x++)
			{
				if (src[x, y].Alpha != 0)
				{
					columnNonZeroAlphas[x]++;
					rowNonZeroAlphas[y]++;	
				}
			}
		}

There are probably slightly more efficient ways, but since it only runs once, and doesn't do anything too complex, it's probably plenty efficient enough.

 

(You don't need to first zero the arrays, since C# zeroes numeric arrays when they're allocated.)

Share this post


Link to post
Share on other sites
2 hours ago, MJW said:

If you need the info for both the rows and columns, the simplest way is probably:


		for (int y = 0; y < height; y++)
		{
			for (int x = 0; x < width; x++)
			{
				if (src[x, y].Alpha != 0)
				{
					columnNonZeroAlphas[x]++;
					rowNonZeroAlphas[y]++;	
				}
			}
		}

There are probably slightly more efficient ways, but since it only runs once, and doesn't do anything too complex, it's probably plenty efficient enough.

 

(You don't need to first zero the arrays, since C# zeroes numeric arrays when they're allocated.)

 

The columnnonzeroalpha[x]++ is something I would have never thought off for generic scripting/programming. Thank you.

Edited by Reptillian

Share this post


Link to post
Share on other sites

Um, I am getting a error here. Otherwise, code looks pretty much correct if you look at the reference code.

 

EDIT: I am getting it to work it seems, just very buggy. :(

 

 

// Name: Shifted Group Pixel to Boundary
// Submenu:
// Author:
// Title:
// Version:
// Desc:
// Keywords:
// URL:
// Help:
#region UICode
DoubleSliderControl positionperc = -100; // [-100,100] Position of Pixels (%)
DoubleSliderControl influenceperc = 100; // [0,100] Influence Factor (%)
IntSliderControl alpha_threshold = 1; // [1,255] Alpha Threshold
ListBoxControl axis = 0; // Axis|Horizontal|Vertical
#endregion

int[] columnNonZeroAlphas = null;
int[] rowNonZeroAlphas = null;

void PreRender(Surface src)
{
    int width = src.Width, height = src.Height;
    if (rowNonZeroAlphas == null)
    {
        for (int y = 0; y < height; y++)
        {
            for (int x = 0; x < width; x++)
            {
               if (src[x,y].A >= alpha_threshold)
               {
                        columnNonZeroAlphas[x]++;
                        rowNonZeroAlphas[y]++;    
               }
            }
        }
    }
}


void Render(Surface dst, Surface src, Rectangle rect)
{
    double position = (positionperc / 100 + 1) / 2;
    double influence_factor = influenceperc / 100;
    double invert_factor = 1 - influence_factor;
    int width = rect.Right;
    int height = rect.Bottom;
    int txx,tyy,nxx,nyy,fx,fy,N;
    double tnyy,tnxx;

    if (axis==0)
        {
            for (int x = 0; x < rect.Right; x++)
            {
                nyy = height - 1;
                if (columnNonZeroAlphas[x] != 0)
                {
                    N=columnNonZeroAlphas[x];
                    tyy = height - N ;
                    nyy-=(int)(tyy*position);
                    for (int y = height - 1; y > - 1; y++)
                    {
                        if (N != 0)
                        {
                            if (src[x,y].A >= alpha_threshold)
                            {
                                N--;
                                tnyy = influence_factor * (double)(nyy) + invert_factor*(double)(y);
                                if (position == 1)
                                {
                                    fy=(int)(Math.Ceiling(tnyy));
                                }
                                else
                                {
                                    fy=(int)(Math.Floor(tnyy));
                                }
                                dst[x,y] = src[x,fy];
                                nyy--;
                            }
                        }
                        else
                        {
                            break;
                        }
                    }
                }
            }
        }
    else
        {
            for (int y = rect.Top; y < rect.Bottom; y++)
            {
                nxx = 0;
                if (rowNonZeroAlphas[y] != 0)
                {
                    N=rowNonZeroAlphas[y];
                    txx = width - N;
                    nxx+=(int)(txx*(position));
                    for (int x = 0; x < rect.Right; x++)
                    {
                        if (N != 0)
                        {
                            if (src[x,y].A >= alpha_threshold)
                            {
                                N--;
                                tnxx = influence_factor * (double)(nxx) + invert_factor*(double)(x);
                                if (position == 1)
                                {
                                    fx=(int)(Math.Floor(tnxx));
                                }
                                else
                                {
                                    fx=(int)(Math.Ceiling(tnxx));
                                }
                                dst[x,y] = src[fx,y];
                                nxx++;
                            }
                        }
                        else
                        {
                            break;
                        }
                    }
                }
            }
        }
}

Edited by Reptillian
Better match to g'mic-qt rep_sptbwgp source code ; Still not there...

Share this post


Link to post
Share on other sites

It's coming along!

 

 

// Name: Shifted Group Pixel to Boundary
// Submenu:
// Author:
// Title:
// Version:
// Desc:
// Keywords:
// URL:
// Help:
#region UICode
DoubleSliderControl positionperc = -100; // [-100,100] Position of Pixels (%)
DoubleSliderControl influenceperc = 100; // [0,100] Influence Factor (%)
IntSliderControl alpha_threshold = 255; // [1,255] Alpha Threshold
ListBoxControl axis = 1; // Axis|Horizontal|Vertical
#endregion

void Render(Surface dst, Surface src, Rectangle rect)
{
    double position = (positionperc / 100 + 1) / 2;
    double influence_factor = influenceperc / 100;
    double invert_factor = 1 - influence_factor;
    int w = rect.Right;
    int h = rect.Bottom;
    int tx,ty,nx,ny,fx,fy,N;
    double tny,tnx;
    
    int[] columnNonZeroAlphas = new int[w];
    int[] rowNonZeroAlphas = new int[h];
    
    if (axis==1)
    {
        for (int x = 0; x < w; x++)
        {
            int nonZeroAlphaCount = 0;
            for (int y = 0; y < h; y++)
            {
                if (src[x,y].A >= alpha_threshold) {nonZeroAlphaCount++;}
                columnNonZeroAlphas[x] = nonZeroAlphaCount;
                dst[x,y]=ColorBgra.FromBgra(0,0,0,0);
            }
        }
    }
    else
    {
        for (int y = 0; y < h; y++)
        {
            int nonZeroAlphaCount = 0;
            for (int x = 0; x < w; x++)
            {
                if (src[x,y].A >= alpha_threshold) {nonZeroAlphaCount++;}
                rowNonZeroAlphas[y] = nonZeroAlphaCount;
                dst[x,y]=ColorBgra.FromBgra(0,0,0,0);
            }
        }
    }

    if (axis==1)
        {
            for (int x = 0; x < w; x++)
            {
                if (IsCancelRequested) return;
                ny = h - 1;
                if (columnNonZeroAlphas[x] != 0)
                {
                    N=columnNonZeroAlphas[x];
                    ty = h - N ;
                    ny-=(int)(ty*position);
                    for (int y = h - 1; y >= 0; y--)
                    {
                        if (N != 0)
                        {
                            if (src[x,y].A >= alpha_threshold)
                            {
                                tny = influence_factor * (double)(ny) + invert_factor*(double)(y);
                                if (position == 1)
                                {
                                fy=(int)(Math.Ceiling(tny));
                                }
                                else
                                {
                                fy=(int)(Math.Floor(tny));
                                }
                                N--;
                                dst[x,fy] = src[x,y];
                                ny--;
                           }
                        }
                        else
                        {break;}
                    }
                }
            }
        }
    else
        {
            for (int y = rect.Top; y < rect.Bottom; y++)
            {
                if (IsCancelRequested) return;
                nx = 0;
                if (rowNonZeroAlphas[y] != 0)
                {
                    N=rowNonZeroAlphas[y];
                    tx = w - N;
                    nx+=(int)(tx*(position));
                    for (int x = 0; x < rect.Right; x++)
                    {
                        if (N != 0)
                        {
                            if (src[x,y].A >= alpha_threshold)
                            {
                                tnx = influence_factor * (double)(nx) + invert_factor*(double)(x);
                                if (position == 1)
                                {
                                fx=(int)(Math.Floor(tnx));
                                }
                                else
                                {
                                fx=(int)(Math.Ceiling(tnx));
                                }
                                N--;
                                dst[fx,y] = src[x,y];
                                nx++;
                            }
                        }
                        else
                        {break;}
                    }
                }
            }
        }
}

 

Edited by Reptillian

Share this post


Link to post
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.