Developing a random string generator for G'MIC-QT

Now, I’m working on making a random string generator which purpose is to create a mathematical equation from existing variables. So, this is useful for making something like random-art.org image generator, inserting custom formulas for filters that has the option, etc… Within random string generator, there will be arithmetic operations and trigometric functions, etc. Feedback is welcome.

WIP function below

rep_random_string:
skip ${2=}
srand $2

n_f={max(1,round(abs($1)))}
iter=0
function_string=""

$=val
targ={narg(${3--1})}
pos={round(u(0,$targ))}
var=${val{$pos+3}}
if !narg($var) var=$3 fi
echo $var

do
 iter+=1
while $iter<$n_f

$1=number of functions
$2=seed
$3=variables

n_f=number of functions/arithmetric argument
targ=total amount of variables argument
pos=position of variables
var=chosen variable based on pos

1 Like

That’s an interesting idea.
In my opinion, it should be coded in a recursive way, to simulate a tree of operations, just like the math parser does its job when analyzing a formula, but instead of using a top-down approach, a random formula generator should use a bottom-up construction, starting from the tree leafs.

So, here is my attempt.
No time tonight to explain all the stuffs that lead to this code, I let you look at yourself and see why this (somehow) works :slight_smile:

Here is the code:

# $1 : probability of a termination node in [0,1]
# $2 : level of recursion (avoid stack overflow due to recursivity)
expr :
  r,nl={[u,$2+1]}
  if (u<$1" && "$2>1)" || "$2>4 # Termination node
    if $r<0.25 u x
    elif $r<0.5 u y
    elif $r<0.75 u c
    else u {_round(u(-5,5),0.1)}
    fi
  else
    p1,p2={u([0.2,0.2],[0.8,0.8])*$1}
    if $r<0.1 u cos(${-expr\ $p1,$nl})
    elif $r<0.2 u sin(${-expr\ $p1,$nl})
    elif $r<0.3 u (${-expr\ $p1,$nl})+(${-expr\ $p2,$nl})
    elif $r<0.4 u (${-expr\ $p1,$nl})-(${-expr\ $p2,$nl})
    elif $r<0.75 u (${-expr\ $p1,$nl})*(${-expr\ $p2,$nl})
    else u (${-expr\ $p1,$nl})/max(0.01,${-expr\ $p2,$nl})
    fi
  fi

test_expr :
  i=0
  for $i<36
    expr=${-expr\ 0.1,0}
    256,256,1,3,"x = 20*(x/w-1); y = 20*(y/h-1);"$expr
    n. 0,255 equalize. 256 r2dx. 128 round.
    if iM-im>200 i+=1 else rm. fi
    e[] "EXPR"$i" = "$expr
  done
  frame 1,1,0 append_tiles ,

And here is the result of

$ gmic text_expr

which is 36 randomly generated images set as tiles for a bigger image.

I’ve tried generating formulas that always generate something visible (and if they finally don’t, the resulting image is discarded).

That was a nice suggestion, thanks @Reptorian!

2 Likes

Quite impressive! Definitely would not need to go to random-art website to do that sort of thing. I was aiming for something different as in the purpose is to create random functions with the option to enable or disable functions/arithmetric operations as inputs, but having two different versions is fine. One for generating random image, and other for generating random equations for inputs.

For single image test. I did this:

test_expr :
val=1
do
 expr=${-expr\ 0.1,-.1}
 $1,$2,1,3,"x = $3*(x/w-1); y = $4*(y/h-1);"$expr
 n. 0,255 equalize. 256
 if iM-im>200 val=0 else rm. fi
 e[] "EXPR"$i" = "$expr
while $val==1

EDIT:

Never mind. I’m trying to make it more random like random-art. Had not figured it out.

My second attempt. This time, I’m generating complex-valued math expressions, and use them to generate independent R,G,B channels, before merging the channels together.
I get more diversity, and with a minimal piece of code. This is fun!

Here is the code:

# $1 : probability of a termination node in [0,1]
# $2 : level of recursion (avoid stack overflow due to recursivity)
expr :
  r,nl={[u,$2+1]}
  if (u<$1" && "$2>1)" || "$2>10 # Termination node
    if $r<0.85 u [X,Y]
    elif $r<0.90 u [c,0]
    elif $r<0.95 u [0,c]
    else u [{_round(u([-15,-15],[15,15]),0.1)}]
    fi
  else # Binary operator
    p1,p2={u([1,1],[2,2])*$1}
    d={1/25}
    if $r<$d u ccos(${-expr\ $p1,$nl})
    elif $r<2*$d u csin(${-expr\ $p1,$nl})
    elif $r<3*$d u ctanh(${-expr\ $p1,$nl})
    elif $r<4*$d u ccosh(${-expr\ $p1,$nl})
    elif $r<5*$d u csinh(${-expr\ $p1,$nl})
    elif $r<6*$d u ctan(${-expr\ $p1,$nl})
    elif $r<7*$d u cexp(${-expr\ $p1,$nl})
    elif $r<8*$d u clog(${-expr\ $p1,$nl})
    elif $r<9*$d u [cabs(${-expr\ $p1,$nl}),0]
    elif $r<10*$d u [0,cabs(${-expr\ $p1,$nl})]
    elif $r<11*$d u [carg(${-expr\ $p1,$nl}),0]
    elif $r<12*$d u [0,carg(${-expr\ $p1,$nl})]
    elif $r<13*$d u cconj(${-expr\ $p1,$nl})
    elif $r<14*$d u (${-expr\ $p1,$nl})+(${-expr\ $p2,$nl})
    elif $r<15*$d u (${-expr\ $p1,$nl})-(${-expr\ $p2,$nl})
    elif $r<16*$d u ${-expr\ $p1,$nl}**${-expr\ $p2,$nl}
    elif $r<17*$d u ${-expr\ $p1,$nl}//${-expr\ $p2,$nl}
    elif $r<18*$d u (${-expr\ $p1,$nl})^^0.5
    elif $r<19*$d u (${-expr\ $p1,$nl})^^2
    elif $r<20*$d u (${-expr\ $p1,$nl})^^3
    elif $r<21*$d u ${-expr\ $p1,$nl}*${-expr\ $p2,$nl}
    elif $r<22*$d u ${-expr\ $p1,$nl}/${-expr\ $p2,$nl}
    elif $r<23*$d u (${-expr\ $p1,$nl})^0.5
    elif $r<24*$d u (${-expr\ $p1,$nl})^2
    else u (${-expr\ $p1,$nl})^3
    fi
  fi

test_expr :
  repeat 36
    e[] "- Generate image \#"$>
    repeat 3
      do
        expr=${-expr\ 0.1,0}
        256,256,1,2,"begin(fx = u(20); fy = u(20)); X = fx*(x/w-0.5) + 0.5/w; Y = fy*(y/h-0.5) + 0.5/h;"$expr
        r={u} if $u<0.3 channels. 0 elif $u<0.6 channel. 1 else norm. fi
        n. 0,255 equalize. 256
        +gradient_norm. cond={sqrt(iv)>2} rm. # Check that image contains enough contours
        if !$cond rm. fi
      while !$cond
    done
    a[-3--1] c r2dx. 128 round.
  done
  frame 1,1,0 append_tiles ,

And here is one result of running command test_expr:

1 Like

It’s a good idea and it’s fun to create these random patterns.
Here is a result obtained with G’MIC-Gimp (41 patterns of 256 * 256).
Thank you :o)

1 Like

Still working on the code, with the idea of converting it to a clean G’MIC-Qt filter in the future.
It starts to look good.

3 Likes

It is really starting to look good. I never gotten that far with modifying code.

In addition, some generated results are even better than random-art image generator. I imagine random-art use post-processing steps to boost the wow factor. Stuff like fill j with a reference layer using draw eclipse.

I’ve added command random_pattern to the G’MIC stdlib (being updated on the G’MIC server, will be available in a few minutes).

   random_pattern:
        _width>0,_height>0

      Insert a new RGB image of specified size at the end of the image list, rendered with a random pattern.
      
      Example: [#1]  repeat 6 random_pattern 256 done

I’ll probably add some more options in the future, but it’s already working as it is, e.g.

$ gmic repeat 60 random_pattern 128 done append_tiles 10,6

gives:

1 Like

EDIT: Now using the text from stdlib. I am finding that it’s not possible to get a seed for the function. The reason I wanted to add that is to allow users to control their output more.

I’ve made some modifications to allow setting a seed and get a consistent result. Anyway, there is command srand that exists to set a random seed, there are no needs to add an extra argument to the random_pattern for that purpose, if the seed can be set prior to the command call.

1 Like

Thank you so much, now it works regardless of size and it’s predictable. I did some bit of modification. Not sure how to preserve the shape while using a non-square size though, and the output is different with original size on.

Why use keep? PDN compatibility while being useful in other programs. Original size should also make it more compatible, and if shape can be preserved, a bonus to Krita and GIMP users as outside of -1,1 boundary can be seen.

#@gui Random Pattern:fx_random_pattern,fx_random_pattern_preview(1)
#@gui :1.Size=_int(1024,16,8192)
#@gui :2.Use Original Image Size?=bool(0)
#@gui :_=separator()
#@gui :3.Min Detail Level=float(4,0,20)
#@gui :4.Seed=float(4038,0,100000)
#@gui :5.Randomize Seed=button()
#@gui :_=separator()
#@gui :Brightness (%)=float(0,-100,100)
#@gui :Contrast (%)=float(0,-100,100)
#@gui :Gamma (%)=float(0,-100,100)
#@gui :Hue (%)=float(0,-100,100)
#@gui :Saturation (%)=float(0,-100,100)
#@gui :_=separator()
#@gui :_=note("<small>Author: <i><a href="http://bit.ly/2CmhX65">David Tschumperl&#233;</a></i>.&#160;&#160;&#160;&#160;&#160;&#160;Latest Update: <i>2020/10/08</i>.</small>")
_fx_random_pattern:
ww={w} hh={h}

if $2 dim=$ww,$hh
else dim=$1,$1
fi

if $5 srand _seed={_round(u(100000))} else _seed=$4 fi
srand $_seed

random_pattern $dim,$3

adjust_colors. ${6-10}

k.

fx_random_pattern :
if $_output_mode rm fi
_fx_random_pattern $*

fx_random_pattern_preview :
_fx_random_pattern {max($_preview_width,$_preview_height)},${2--1}
k[0] rr2d $_preview_width,$_preview_height,2,2
to "Seed: \#"$_seed,5,5,5%,2
u "{$1}_"{$2?1:2}\
"{$2}"\
"{$3}"\
"{"$_seed"}"\
"{0}"\
"{$6}"\
"{$7}"\
"{$8}"\
"{$9}"\
"{$10}"

Coming out of hiding. Is there a way to set the ranges of hues used? Sometimes monochrome images are nicer.

To_gray is the easiest approach. If I feel like it, I may extend color space modes into random function.