Attempt at cubism

Would this help?

Then use N to do your calculations.

I assume that N would be a vector and I’d need to set S to 1. Thanks for giving me those lines of code! I had no idea it would be that easy…

I’ll need to also use the entire cropped region with another grid region to put a different emphasis on diagonals, which should also be easy. Let’s say that if, in a 3x3 grid, 5 pixels around the central pixel have the same label id as the central one, the central one will be left unchanged. That’s good for keeping crisp horizontal and vertical edges but diagonal ones wouldn’t be so nice. Keeping sharp corners would also be hard unless I use a larger grid.

Edit: I’ll need to find a way to take all cases into account - for example, if there are 3 ids in the grid. Maybe it’s not as complicated as I think it’ll be. Here’s the code for the f block anyway. Since I needed to change more than the f block in the end, here’s the whole command without any grid stuff.

#@gui Cubism: fx_jr_cubism_preview
#@gui : sep = separator()
#@gui : 0. Recompute = button()
#@gui : 1. Threshold=float(2,0,15)
#@gui : 2. Smoothness=float(1,0,500)
#@gui : 3. Offset Amplitude=float(10,0,100)
fx_jr_cubism:
ww={w}
hh={h}
+b. $2 segment_watershed. $1
f. ">begin(srand();rand=u(100,200));srand(i*rand);u"
repeat {s}
sh. $> *. {u(1,2)} rm.
done
to_gray.
label.
initsize={1+iM#1}
$initsize,5,1,1
vsize={$initsize*2}
eval ${-math_lib}"
coordinates_per_val=vector"$vsize"(0);
count_per_val=vector"$initsize"(0);
for(px=0,px<w#1,px++,
    for(py=0,py<h#1,py++,
        pos=i(#1,px,py);
        coordinates_per_val[pos*2]+=px;
        coordinates_per_val[pos*2+1]+=py;
        count_per_val[pos]++;
    );
);
for(n=0,n<"$initsize",n++,
    coordinates_per_val[n*2]/=count_per_val[n];
    coordinates_per_val[n*2+1]/=count_per_val[n];
	I(n,0)=coordinates_per_val[n*2];
	I(n,1)=coordinates_per_val[n*2+1];
);
"
repeat {3}
row={$>+2}
sh. $row,$row,0,0 f. ">begin(srand();randm=u(100,200);randa=u(1,3));srand((x+(randa))*randm);u"
if {$>==2} n. 0,{2*pi} else n. 0,1 fi
rm.
done
amp={$3*norm(w,h)*0.01}
f[0] "begin(amp="$amp");
cubism(id,xx,yy)=(
xoff=i(#2,id,2,0,0,1)*amp;
yoff=i(#2,id,3,0,0,1)*amp;
ang=i(#2,id,4,0,0,1);
sang=sin(ang);
cang=cos(ang);
xro=i(#2,id,0,0,0,1);
yro=i(#2,id,1,0,0,1);
vx=xx-xro-xoff;
vy=yy-yro-yoff;
I(vx*cang+vy*sang+xoff+xro,vy*cang-vx*sang+yoff+yro,0,3,3));
cubism(i(#1,x,y,0,0,1),x,y)
"
rm[^0]
fx_jr_cubism_preview:
fx_jr_cubism ${2-4}

I do not know what you are doing, but making it complex is fine. You may find a easier solution down the road. I did complicated solution, but refactored and found more sensible solution on some of my filters.

I need even more help. I need to build a histogram of all the values within the grid, with the frequencies taking into account the grid weighting. For now I want the weighting to be [2,1,2,1,4,1,2,1,2]/16. It will help me to reduce the number of times thecubism() functon gets called since it most likely won’t be dealing with unique label ids for each pixel.

I only figure it out after I ask. I don’t know why.

Also how does sort() work when I specify a chunk size? The technical reference never says anything about that.

Anyway, I would really like a simple histogram() function which lets me specify both a data array and a frequency weighting array. That would make this much easier.

I will never be able to figure out why this doesn’t work. This should be absolutely perfect as far as I see but it’s not. The idlist vector always ends up being empty and I can never figure out why. It always feels like this happens to me.

Edit: it’s always the small things - it was the wrong comparison operator in both for loops. As usual, I only find out after I moan about it.

#@gui Cubism: fx_jr_cubism_preview
#@gui : sep = separator()
#@gui : 0. Recompute = button()
#@gui : 1. Threshold=float(2,0,15)
#@gui : 2. Smoothness=float(1,0,500)
#@gui : 3. Offset Amplitude=float(10,0,100)
#@gui : 4. Anti-Aliasing=bool(1)
fx_jr_cubism:
ww={w}
hh={h}
aa=$4
amp={$3*norm(w,h)*0.01}
+b. $2 segment_watershed. $1
f. ">begin(srand();rand=u(100,200));srand(i*rand);u"
repeat {s}
sh. $> *. {u(1,2)} rm.
done
to_gray.
label.
initsize={1+iM#1}
$initsize,5,1,1
vsize={$initsize*2}
eval ${-math_lib}"
coordinates_per_val=vector"$vsize"(0);
count_per_val=vector"$initsize"(0);
for(px=0,px<w#1,px++,
    for(py=0,py<h#1,py++,
        pos=i(#1,px,py);
        coordinates_per_val[pos*2]+=px;
        coordinates_per_val[pos*2+1]+=py;
        count_per_val[pos]++;
    );
);
for(n=0,n<"$initsize",n++,
    coordinates_per_val[n*2]/=count_per_val[n];
    coordinates_per_val[n*2+1]/=count_per_val[n];
	I(n,0)=coordinates_per_val[n*2];
	I(n,1)=coordinates_per_val[n*2+1];
);
"
repeat {3}
row={$>+2}
sh. $row,$row,0,0 f. ">begin(randm=u(100+y,200+y);randa=u(1,3));srand((x+(randa))*randm);u"
if {$>==2} n. 0,{2*pi} else n. 0,$amp fi
rm.
done
f[0] "begin(const spec=s;
cubism(id,xx,yy)=(
params=crop(#2,id,0,0,0,1,5,1,1);
xro=params[0];
yro=params[1];
xoff=params[2];
yoff=params[3];
ang=params[4];
sang=sin(ang);
cang=cos(ang);
vx=xx-xro-xoff;
vy=yy-yro-yoff;
I(vx*cang+vy*sang+xoff+xro,vy*cang-vx*sang+yoff+yro,0,1,3));
gridmult=[1,2,1,2,4,2,1,2,1]*0.0625);
ref(crop(#1,x-1,y-1,0,c,3,3,1,1,1),grid);
"$aa"?(
idlist=vector(#9,-1);
freqlist=vector(#9,0);
idnum=0;
for(n=0,n<9,n++,
	curr=grid[n];
	pos=find(idlist,curr);
	pos==-1?(idnum+=1;pos=find(idlist,-1);idlist[pos]=curr);
	freqlist[pos]+=gridmult[n];
);
val=vector(#spec,0);
for(n=0,n<9,n++,
val+=cubism(idlist[n],x,y)*freqlist[n]);
val):(cubism(i(#1,x,y,0,0,1),x,y))
"
rm[^0]

fx_jr_cubism_preview:
fx_jr_cubism ${2-5}

It can blur the edges but many edges that should be sharp are not. Let’s see how I can fix this.

Consider anisotropic filtering.

I don’t know how I’d use anisotropic filtering since we’re talking about 2D shapes and not about viewing textures in 3D space.

What complicates this even more is that I have no idea how I’d use the usual smoothing algorithms besides linear interpolation. They deal directly with pixel intensities rather than transitions between pixel values which correspond with label ids. I can’t interpolate between the IDs themselves because that would make the angles and offsets all messed up between the segments, causing smearing effects (which I don’t want), and I can’t perform any AA on the final pixel values because that would just blur the result.

There is a way that I had thought of some time ago but wanted to avoid: splitting the label image across the z axis and setting the non-zero pixel values to 1, performing anti-aliasing on each z plane separately to get smoothed planes of intensity for each label id, and then finding the non-zero intensities for each id for each pixel in the fill block that follows! I think it would be incredibly wasteful and also block any extensibility to generating 3D cubist images.

I was reading this though: Conservative Morphological Anti-Aliasing (CMAA), which uses a post-processing algorithm to detect sharp lines. Somehow I could use gradient detection here in a similar way, but not for post-processing the final image…

Actually, I did something like that. I did release a bit of a wip sample to find angles of gradient along 4 vectors in my gmiccollection.zip which can be found in my thread. Would that help?

Which file within that zip is it in?

Clipwarpcreation. atan2(a,b) is used to find the angle in the end.

Okay. Yet another method I have in mind is this: GBAA: Geometry Buffer Anti-Aliasing | Geeks3D

I could use what you’ve made to generate edge planes, detect diagonal edges and then blur along those diagonal edges.

Another resource I found on this is here: http://www.iryoku.com/aacourse/downloads/Filtering-Approaches-for-Real-Time-Anti-Aliasing.pdf

1 Like

Anisotropy can apply to 2d surfaces (e.g. bilateral and afre_gui*), though I agree that filters tend to shift edges one way or another. Also try antialiasing by iterating certain edge-aware filters (wait, maybe that aliases the edges… I forget – ha ha).

That reminds me of

#@cli autocrop_components : _threshold[%],_min_area[%]>=0,_is_high_connectivity={ 0 | 1 },\
# _output_type={ 0=crop | 1=segmentation | 2=coordinates }
#@cli : Autocrop and extract connected components in selected images, according to a mask given as the last channel of
#@cli : each of the selected image (e.g. alpha-channel).
#@cli : Default values: 'threshold=0%', 'min_area=0.1%', 'is_high_connectivity=0' and 'output_type=1'.
#@cli : $ 256,256 noise 0.1,2 eq 1 dilate_circ 20 label_fg 0,1 normalize 0,255 +neq 0 *[-1] 255 append c \
# +autocrop_components ,
autocrop_components : skip ${1=0%} check "${2=0.1%}>=0 && isbool(${3=0}) && isint(${4=1}) && $4>=0 && $4<=2"
  e[^-1] "Autocrop connected components from image$?, with threshold $1, minimal area $2, "\
         ${arg\ 1+$3,low,high}" connectivity "\
         "and output type set to '"${arg\ 1+$4,crop,segmentation,coordinates}"'.\n"
  repeat $! l[$>]
    min_area={max(1,round(if(${is_percent\ $2},$2*w*h,$2)))}
    +channels 100% >. $1 area_fg. 0,$3 >=. $min_area  # Discard background and small objects.
    +area. 0,1 <. $min_area -|[-2,-1] label_fg. 0,1    # Fill small holes in objects.

    # Extract detected objects.
    N={iM} repeat iM
      n={1+$>}
      e[] "\r  > "$n/$N
      rprogress {100*$n/$N}
      +==[1] $n +*[0,-1] rm..
      if $4==0 coords=${autocrop_coords.\ auto} rm. +z[0] $coords
      elif $4==1 autocrop.
      else coords=${autocrop_coords.\ auto} rm. ($coords) y.
      fi
    done
    rm[0,1]
    if !$! 0 fi
    if $4==2 a x fi
  endl done
1 Like

Hey, that’s a useful command. I might use that in future. I’ll definitely be switching to an MLAA algorithm for the cubism script though.

My current plan is to generate an edge map, a directional edge map and a blurred version of the directional edge map which I’ll multiply and add together.

I also found this: CMAA2/CMAA2.hlsl at master · GameTechDev/CMAA2 · GitHub

This could give me some help in figuring out how to do that. I’ve made a relevant edge-detection script here:


+to_rgba f. 0
f. "val=vector4(0);
I(#0)!=J(#0,1)?(val[0]+=1]);
I(#0)!=J(#0,0,1)?(val[1]+=1]);
I(#0)!=J(#0,-1)?(val[2]+=1]);
I(#0)!=J(#0,0,-1)?(val[3]+=1]);
val"

n. 0,255

100%,100%,1,4,(0)
f. "begin(boundary=1);
val=vector4(0);
I(#0)!=J(#0,1)?(val[0]+=1);
I(#0)!=J(#0,0,1)?(val[1]+=1);
I(#0)!=J(#0,-1)?(val[2]+=1);
I(#0)!=J(#0,0,-1)?(val[3]+=1);
val"
+boxfilter. 2
*. ..
n. 0,1
-.. .
k..
^ 4
display

Edit: here’s an improvement on that. Still a few things to sort out.

+to_graya f. 0
f. "val=vector2(0);
I(#0)!=J(#0,1)?(val[0]+=1);
I(#0)!=J(#0,0,1)?(val[1]+=1);
I(#0)!=J(#0,-1)?(val[0]-=1);
I(#0)!=J(#0,0,-1)?(val[1]-=1);
val"

*. 256
+smooth. 10,0,1,10,10,15,0
-.. . k.. *. 0.00390625

Another try now, which as usual I won’t be able to fix unless I complain about it:

#@gui Cubism: fx_jr_cubism_preview
#@gui : sep = separator()
#@gui : 0. Recompute = button()
#@gui : 1. Threshold=float(2,0,15)
#@gui : 2. Smoothness=float(1,0,500)
#@gui : 3. Offset Amplitude=float(10,0,100)
#@gui : 4. Anti-Aliasing=bool(1)
fx_jr_cubism:
ww={w}
hh={h}
aa=$4
amp={$3*norm(w,h)*0.01}
+b. $2 segment_watershed. $1
f. ">begin(srand();rand=u(100,200));srand(i*rand);u"
repeat {s}
sh. $> *. {u(1,2)} rm.
done
to_gray.
label.
initsize={1+iM#1}
$initsize,5,1,1
vsize={$initsize*2}
eval ${-math_lib}"
coordinates_per_val=vector"$vsize"(0);
count_per_val=vector"$initsize"(0);
for(px=0,px<w#1,px++,
    for(py=0,py<h#1,py++,
        pos=i(#1,px,py);
        coordinates_per_val[pos*2]+=px;
        coordinates_per_val[pos*2+1]+=py;
        count_per_val[pos]++;
    );
);
for(n=0,n<"$initsize",n++,
    coordinates_per_val[n*2]/=count_per_val[n];
    coordinates_per_val[n*2+1]/=count_per_val[n];
	I(n,0)=coordinates_per_val[n*2];
	I(n,1)=coordinates_per_val[n*2+1];
);
"
repeat {3}
row={$>+2}
sh. $row,$row,0,0 f. ">begin(randm=u(100+y,200+y);randa=u(1,3));srand((x+(randa))*randm);u"
if {$>==2} n. 0,{2*pi} else n. 0,$amp fi
rm.
done
$ww,$hh,1,2
f. "val=vector2(0);
I(#1)!=J(#1,1)?(val[0]+=1);
I(#1)!=J(#1,0,1)?(val[1]+=1);
I(#1)!=J(#1,-1)?(val[0]-=1);
I(#1)!=J(#1,0,-1)?(val[1]-=1);
val"
*. 255
+smooth. 10,0,1,1,5,15,0
-.. .
rm.
/. 255
f. "sign(i)*(min(1,max(0,abs(i)-0.2)*1.25)^0.25)"
f[0] "begin(const spec=s;
cubism(id,xx,yy)=(
params=crop(#2,id,0,0,0,1,5,1,1);
xro=params[0];
yro=params[1];
xoff=params[2];
yoff=params[3];
ang=params[4];
sang=sin(ang);
cang=cos(ang);
vx=xx-xro-xoff;
vy=yy-yro-yoff;
I(vx*cang+vy*sang+xoff+xro,vy*cang-vx*sang+yoff+yro,0,1,3)));
"$aa"?(
idlist=vector(#4,-1);
freqlist=vector(#4,0);
idnum=0;
xs=i(#-1,x,y,0,0);
ys=i(#-1,x,y,0,1);
xdir=(xs>=0?(1):(-1));
ydir=(ys>=0?(1):(-1));
gridmult=[1-((1-xs)*(1-ys)),xs,ys,xs*ys];
gpos=0;
for(py=0,py<2,py++,
	for(px=0,px<2,px++,
		curr=i(#1,x+xdir*px,y+ydir*py,0,0,1);
		pos=find(idlist,curr);
		pos==-1?(idnum+=1;pos=find(idlist,-1);idlist[pos]=curr);
		freqlist[pos]+=gridmult[gpos];
		gpos+=1
	);
);
val=vector(#spec,0);
for(n=0,n<idnum,n++,
val+=cubism(idlist[n],x,y)*freqlist[n]);
val):(cubism(i(#1,x,y,0,0,1),x,y))
"
rm[^0]

fx_jr_cubism_preview:
fx_jr_cubism ${2-5}

Bugs:

  1. Anti-alias doesn’t work on Krita. It leads to a nearly empty image. Nice cobweb though.
  2. Sometimes changing smoothness leads to wacky structures.

The first bug is one that I’ve also found straightaway and I have no idea how I’ll fix that right now. However, now that you mention it I could repurpose parts of it to make a separate cobweb script.

I’ve got rid of the cobweb bug, that was due to me not understanding how bilinear interpolation works. Here’s the last fill block again:

f[0] "begin(const spec=s;
cubism(id,xx,yy)=(
params=crop(#2,id,0,0,0,1,5,1,1);
xro=params[0];
yro=params[1];
xoff=params[2];
yoff=params[3];
ang=params[4];
sang=sin(ang);
cang=cos(ang);
vx=xx-xro-xoff;
vy=yy-yro-yoff;
I(vx*cang+vy*sang+xoff+xro,vy*cang-vx*sang+yoff+yro,0,1,3)));
xs=i(#-1,x,y,0,0);
ys=i(#-1,x,y,0,1);
("$aa"&&xs!=0&&ys!=0)?(
idlist=vector(#4,-1);
freqlist=vector(#4,0);
idnum=0;
xdir=(xs>=0?(1):(-1));
ydir=(ys>=0?(1):(-1));
xs=abs(xs);ys=abs(ys);
gridmult=[(1-xs)*(1-ys),xs*(1-ys),(1-xs)*ys,xs*ys];
for(py=0,py<2,py++,
	for(px=0,px<2,px++,
		curr=i(#1,x+xdir*px,y+ydir*py,0,0,1);
		pos=find(idlist,curr);
		pos==-1?(idnum+=1;pos=find(idlist,-1);idlist[pos]=curr);
		freqlist[pos]+=gridmult[py*2+px];
	);
);
val=vector(#spec,0);
for(n=0,n<idnum,n++,
val+=cubism(idlist[n],x,y)*freqlist[n]);
val):(cubism(i(#1,x,y,0,0,1),x,y))
"

Now there’s another problem: the edges of each segment have become fuzzy as well as jagged!

Finally, something with working anti-aliasing! I’ve put it elsewhere to stop spamming this thread too much: #@gui Cubism: fx_jr_cubism_preview#@gui : sep = separator()#@gui : 0. Recomp - Pastebin.com

I’ll improve this more if I find a way. My focus will switch to making it work for 3D images and adding a custom map feature too. I could even add a custom blend map thing too for custom anti-aliasing purposes…

I think this’ll do for now. 3D images and custom map is ambituous.