'Glitch' Art Filters - again

The massive slowdown comes from apply_tiles. I don’t get where the idct comes from though and what it’s suppose to do. Maybe with that info I could come up with a theory on how to make a faster version, but you already have a version that’s 15 times faster.

Also, here’s the analysis of difference/xor if that would help

image

XOR Analysis

image

Difference analysis

Thanks. While you were replying I updated it because I found something after doing some research. What I’ve got now is almost like what I want but there’s a constant factor missing and I’m not sure how this constant factor varies.

I think you got it actually with the new update. Blend difference reveals this when comparing normalized images.

[2] = '[unnamed]_c1':
  size = (64,64,1,1) [16 Kio of floats].
  data = (0,0,0,0,0,0,0,0,0,0,1.52588e-05,1.52588e-05,(...),0,7.62939e-06,0,7.62939e-06,7.62939e-06,1.52588e-05,7.62939e-06,1.52588e-05,7.62939e-06,0,7.62939e-06,0).
  min = 0, max = 2.28882e-05, mean = 7.56842e-06, std = 6.54397e-06, coords_min = (0,0,0,0), coords_max = (13,8,0,0).

Now it’s just a matter of normalizing your result.

The constant factor I was looking for is 64, but I can always take that out. I now have something from which I can easily make an IDCT, but in his command, David left out the DCT entirely. I have to figure out how he did that.

rm
tw=8
th=8
{$tw^2},{$th^2},1,1
f "begin(tw="$tw";th="$th");
tx=(x-(x%tw))/tw;
ty=(y-(y%th))/th;
cx=cos((2*x+1)*tx*pi*0.5/tw)/sqrt(tw);
cy=cos((2*y+1)*ty*pi*0.5/th)/sqrt(th);
tx!=0?(cx*=sqrt(2));
ty!=0?(cy*=sqrt(2));
val=cx*cy;
((tx+ty)%2)?(val*=-1);
val*64;
"

dct = discrete cosine transform
idct = inverse of that

2 Likes

I figure that I can use these tiles for both the DCT and the IDCT…

I want this filter to be able to run on 3-channel images, with a separate DCT tile size for each channel. I would need to generate up to three DCT tile sets but if two or more channels have the same tile sizes, I could just re-use them. How would I be able to do this? I need to:

  • make the required tile sets, up to three of them
  • use the correct tile sets for each channel

I also have a broken implementation with a fixed tile size for all channels. As usual, unless I seek attention and post it first, I will never figure it out.

dct_tileset:
{$1^2},{$2^2},1,1
f. "begin(tw=$1;th=$2);
tx=(x-(x%tw))/tw;
ty=(y-(y%th))/th;
cx=cos((2*x+1)*tx*pi*0.5/tw);
cy=cos((2*y+1)*ty*pi*0.5/th);
val=cx*cy;
((tx+ty)%2)?(val*=-1);
val
"

fx_jpeg :

to_rgb
tw=8
th=8
dct_tileset $tw,$th

nm. cosines
repeat {$!-1} l[$>,cosines]
ww={w(#0)}
hh={h(#0)}
r[0] {w(#0)+(-w(#0)%$tw)},{h(#0)+(-h(#0)%$th)},1,100%,0,3

s[0] c

repeat 3
[$>]
f[$>] >"
begin_t(const tw="$tw";const th="$th";const twh=tw*th);
if (!(x%tw) && !(y%th),
res=vector(#twh,0);
ref(crop(x,y,tw,th),src);
for (l = 0, l<th, ++l, for (k = 0, k<tw, ++k, off = k + th*l; res += src[off]*crop(#"$cosines",k*tw,l*th,tw,th,1)));
draw(#-1,res,x,y,0,0,tw,th);
); i"
round.
f[$>] >"
begin_t(const tw="$tw";const th="$th";const twh=tw*th;res=vector(#twh));
if (!(x%tw) && !(y%th),
res=vector(#twh,0);
ref(crop(x,y,tw,th),src);
for (l = 0, l<th, ++l, for (k = 0, k<tw, ++k, off = k + th*l; res += src[off]*crop(#"$cosines",k*tw,l*th,tw,th,1)));
draw(#"$>",res,x,y,0,0,tw,th);
); i"
rm.

done
a[0-2] c
r[0] $ww,$hh,1,100%,0,3
endl done

Inserting : before begin on dct_tileset gives 0 s run instead of .16 s. I’ll figure the channels part for you.

EDIT: I don’t think I can figure it out. Maybe has to do with crop. Try crop(x,y,z,c,w,h,d,s) instead of crop(x,y,w,h)

EDIT 2: There is also the option of using a smaller image to manipulate a bigger image. Fill can be used to manipulate outside image. See ‘rep_recc’.

I realise that I am actually running two IDCTs. I can generate a tile set for a DCT but I want to figure out a faster way.

rm
tw=8
th=8
{$tw^2},{$th^2},1,1
+f "!((x%("$tw"+1))||(y%("$th"+1)))?1" at. "dct",$tw,$th
n 0,255
display

Edit: I’ve figured out how to make the DCT tileset, and now the encoder is working for square tiles. For some reason it doesn’t work for non-square tiles.

dct_tileset:
{$1^2},{$2^2},1,1
f. :"begin(const tw=$1;const th=$2;const stw=sqrt(tw);const sth=sqrt(th);const itw=1/tw;const ith=1/th;const istw=sqrt(itw);const isth=sqrt(ith);const s2=sqrt(2));
px=x%tw;
py=y%th;
tx=(x-(px))*itw;
ty=(y-(py))*ith;
cx=cos((tx*0.5+((tx+0.5)*x*itw))*pi)*istw;
cy=cos((ty*0.5+((ty+0.5)*y*ith))*pi)*isth;
px!=0?(cx*=s2);
py!=0?(cy*=s2);
val=cx*cy;
val;
"

idct_tileset:
{$1^2},{$2^2},1,1
f. :"begin(const tw=$1;const th=$2;const stw=sqrt(tw);const sth=sqrt(th);const itw=1/tw;const ith=1/th;const istw=sqrt(itw);const isth=sqrt(ith);const s2=sqrt(2));
px=x%tw;
py=y%th;
tx=(x-(px))*itw;
ty=(y-(py))*ith;
cx=cos((x+0.5)*tx*pi*itw)*istw;
cy=cos((y+0.5)*ty*pi*ith)*isth;
tx!=0?(cx*=s2);
ty!=0?(cy*=s2);
val=cx*cy;
((tx+ty)%2)?(val*=-1);
val;
"

fx_jpeg :

to_rgb
tw=8
th=8
dct_tileset $tw,$th
idct_tileset $tw,$th
nm[-2,-1] dct,idct

repeat {$!-2} l[$>,dct,idct]
ww={w(#0)}
hh={h(#0)}
r[0] {w(#0)+(-w(#0)%$tw)},{h(#0)+(-h(#0)%$th)},1,100%,0,3

s[0] c

repeat 3
[$>]
f[$>] :"
begin_t(const tw="$tw";const th="$th";const twh=tw*th);
if (!(x%tw) && !(y%th),
res=vector(#twh,0);
ref(crop(x,y,tw,th),src);
for (l = 0, l<th, ++l, for (k = 0, k<tw, ++k, off = k + th*l; res += src[off]*crop(#"$dct",k*tw,l*th,tw,th,1)));
draw(#-1,res,x,y,0,0,tw,th);
); i"

f. :"
begin_t(const tw="$tw";const th="$th";const twh=tw*th;res=vector(#twh));
if (!(x%tw) && !(y%th),
res=vector(#twh,0);
ref(crop(x,y,tw,th),src);
for (l = 0, l<th, ++l, for (k = 0, k<tw, ++k, off = k + th*l; res += src[off]*crop(#"$idct",k*tw,l*th,tw,th,1)));
draw(#"$>",res,x,y,0,0,tw,th);
); i"

rm.
done
a[0-2] c
r[0] {$ww},{$hh},1,100%,0,3
endl done
rm[dct,idct]
display

When the second dimension is smaller than the first, it looks all wrong. When it’s larger, all the values in the resulting image turn to NaN.

Edit: a simple fix, I should’ve used tw and not th.

fx_jpeg :

to_rgb
tw=$1
th=$2
dct_tileset $tw,$th
idct_tileset $tw,$th
nm[-2,-1] dct,idct

repeat {$!-2} l[$>,dct,idct]
ww={w(#0)}
hh={h(#0)}
r[0] {w(#0)+(-w(#0)%$tw)},{h(#0)+(-h(#0)%$th)},1,100%,0,3

s[0] c

repeat 3
[$>]
f[$>] :"
begin_t(const tw="$tw";const th="$th";const twh=tw*th);
if (!(x%tw) && !(y%th),
res=vector(#twh,0);
ref(crop(x,y,tw,th),src);
for (l = 0, l<th, ++l, for (k = 0, k<tw, ++k,
off = k + tw*l;
res += src[off]*crop(#"$dct",k*tw,l*th,tw,th,1)));
draw(#-1,res,x,y,0,0,tw,th);
); i"

f. :"
begin_t(const tw="$tw";const th="$th";const twh=tw*th;res=vector(#twh));
if (!(x%tw) && !(y%th),
res=vector(#twh,0);
ref(crop(x,y,tw,th),src);
for (l = 0, l<th, ++l, for (k = 0, k<tw, ++k,
off = k + tw*l;
res += src[off]*crop(#"$idct",k*tw,l*th,tw,th,1)));
draw(#"$>",res,x,y,0,0,tw,th);
); i"
rm.
done
a[0-2] c
r[0] {$ww},{$hh},1,100%,0,3
endl done
rm[dct,idct]

@Joan_Rake1

Made some changes to speed it up a bit.

fx_jpeg :

tw=$1
th=$2
dct_tileset $tw,$th
idct_tileset $tw,$th
nm[-2,-1] dct,idct

repeat {$!-2} l[$>,dct,idct]
 ww={w(#0)}
 hh={h(#0)}
 r[0] {w(#0)+(-w(#0)%$tw)},{h(#0)+(-h(#0)%$th)},1,100%,0,3

 {w#0},{h#0},{d#0},{s#0}

 f[0] :"
  begin_t(const tw="$tw";const th="$th";const twh=tw*th);
  if (!(x%tw) && !(y%th),
  res=vector(#twh,0);
  ref(crop(x,y,z,c,tw,th,1,1),src);
  for (l = 0, l<th, ++l, 
   for (k = 0, k<tw, ++k,
    off = k + tw*l;
    res += src[off]*crop(#"$dct",k*tw,l*th,tw,th,1)
   );
  );
  draw(#-1,res,x,y,z,c,tw,th,1,1);
  );i"

 f. :"
  begin_t(const tw="$tw";const th="$th";const twh=tw*th;res=vector(#twh));
  if (!(x%tw) && !(y%th),
   res=vector(#twh,0);
   ref(crop(x,y,z,c,tw,th,1,1),src);
   for (l = 0, l<th, ++l, for (k = 0, k<tw, ++k,
   off = k + tw*l;
   res += src[off]*crop(#"$idct",k*tw,l*th,tw,th,1)));
   draw(#0,res,x,y,z,c,tw,th,1,1);
  );i"
  rm.

 r[0] {$ww},{$hh},1,100%,0,3
endl done
rm[dct,idct]

I might try to code up using small image to speed it up and eliminate the !(x%tw) && !(y%th).

Thanks for this, but I need to keep the channels separate so that I can use different tile sizes for each of them in future. I might try the other optimisation you’re talking about. In the meantime, here’s a new version with a quality setting:

fx_jpeg :

to_rgb
tw=$1
th=$2
q=$3
dct_tileset $tw,$th
idct_tileset $tw,$th
nm[-2,-1] dct,idct

repeat {$!-2} l[$>,dct,idct]
ww={w(#0)}
hh={h(#0)}
r[0] {w(#0)+(-w(#0)%$tw)},{h(#0)+(-h(#0)%$th)},1,100%,0,3

csswap[0] 0,15 s[0] c 

repeat 3
{w#0},{h#0},{d#0},{s#0}
f[$>] :"
begin_t(const tw="$tw";const th="$th";const twh=tw*th);
if (!(x%tw) && !(y%th),
res=vector(#twh,0);
ref(crop(x,y,tw,th),src);
for (l = 0, l<th, ++l, for (k = 0, k<tw, ++k,
off = k + tw*l;
res += src[off]*crop(#"$dct",k*tw,l*th,tw,th,1)));
draw(#-1,res,x,y,0,0,tw,th);
); i"

f. :"begin_t(const qual=1024*(1-"$q"*0.01)^3);round(i,qual)"

f. :"
begin_t(const tw="$tw";const th="$th";const twh=tw*th;res=vector(#twh));
if (!(x%tw) && !(y%th),
res=vector(#twh,0);
ref(crop(x,y,tw,th),src);
for (l = 0, l<th, ++l, for (k = 0, k<tw, ++k,
off = k + tw*l;
res += src[off]*crop(#"$idct",k*tw,l*th,tw,th,1)));
draw(#"$>",res,x,y,0,0,tw,th);
); i"
rm.
done
a[0-2] c csswap[0] 15,0
r[0] {$ww},{$hh},1,100%,0,3
endl done
rm[dct,idct]

What I would do is put $3 into $1, and then utilize vectorv($2–1) where v is size or variable that represent size to allow multiple inputs (tiles per channels). Just from your code, I can see that you need to adjust the code a lot to utilize separate tile size per channel and have the best optimum speed possible.

I don’t know what you mean by this.

I also want to specify different tile offsets for each channel so it’s as if the image has been cropped.

I’m saying that you should use vectors to do that task. $2–1 means from second variable to last variables. This makes it possible to do what you want. You can access element of vectors out of eval/fill via {($vector)[pos]} or {($vector)[pos,size]}.

1 Like

I understand. Now I have to find a way to get it to check for duplicates in that list, create the right number of tilesets and then assign the right ones to it. I could name the tilesets to match the channels which will be using them.

vec={[2,3,4]}
+. {$vec[0]}

Here, {$vec[0]} becomes {2,3,4[0]}, not 2. How do I get the result I want?

Easy. Parenthesis.

1,1,1,1 
vec=[2,3,4] 
+. {($vec)[0]}

#2#

For more variables:

$ rep_vec 5,4,3,1 
#4#
rep_vec:
1,1,1,1
vec=[${1-3}]
+. {($vec)[$4]}
1 Like

Thank you so much! What about manipulating subsets of vectors in the pipeline? I want to change a vector one element at a time.

I think multiple eval would be the answer to your need. I haven’t managed to manipulate multiple vector variables and output them into a variable within one eval. Not even run(‘variable=[${}]’) works. Gonna ask @David_Tschumperle on how to do that.

rep_vec:
eval "
 v_tile_info=[${2--1}];
 const sub_v_s=2;
 const v_s=size(v_tile_info)/sub_v_s;
 v_tile_duplicates=vectorv_s(0);
 v_tile_duplicates_loc=vectorv_s(0);
 for(n=1,n<v_s,n++,
  for(m=0,m<n,m++,
   if(v_tile_info[n*sub_v_s,sub_v_s]==v_tile_info[m*sub_v_s,sub_v_s],
    v_tile_duplicates[n]=1;
    v_tile_duplicates_loc[n]=sub_v_s*m;
    break();
   );
  );
 );
 v_tile_duplicates_loc;"

v_tile_duplicates_loc=[${}]
f "
  V=[1,2,3,4];
  V[0]=100; W=V[0,2]; V[1]=sum(W);
  print(V)    # V = [ 100,102,3,4 ]
"

describes a vector V of 4 elements. Set element #0 to 100, and element #1 as sum of new element #0 and original element #1.