How to find out cut values by amount of pixels and has g'mic a ROI?

Hi,

Thanks for your great software.

I have two questions:
If you want to do contrast stretching you normally neglect pixels at the lower and upper edges which only represent e.g. 0.5% of the sum of the image pixels. I.e. if I have an 1Mpx image I would cut the edges in such a way that 990k values are preserved. But how do I find these values in a range of 0-255 (RGB) or 0-100% (HSL)?

If I just want to get these values above out of a cropped image it would be better to have a kind of range of interest (ROI like opencv has) instead of wasting cpu power by making a copy, crop it, cut it, extract some information in a variable and delete the copy afterwards. Does g’mic has such ROI feature?

TIA,
Guenther

There isn’t a ROI feature, but you can get a cropped info in a variable.

crop(_#ind,_x,_y,_z,_c,_dx,_dy,_dz,_dc,_boundary_conditions) returns a vector whose 
          values come from the cropped region of image [ind] (or from default image selected if 
          ind is not specified). Cropped region starts from point (x,y,z,c) and has a size of 
          dx x dy x dz x dc. Arguments for coordinates and sizes can be omitted if they are not 
          ambiguous (e.g. crop(#ind,x,y,dx,dy) is a valid invocation of this function). 

If you want to crop for individual channels instead, then you do something like.

repeat $! l[$>]
 repeat s
  sh. $>
  #Insert the use of crop function in eval or fill block loop or a variable#
  rm.
 done
endl done
  1. s is the number of channels in a image.
  2. shared is directly accessing a channel by creating a linked layer. It’s not creating a new image.
  3. rm. removed the shared layer.

Here’s a real world example of crop usage

 f. ">begin(const ww=abs($3);const hh=abs($4);const wwhh=ww*hh;const length=floor(w/ww);avg_a=vectorlength(0);avg_b=vectorlength(0);const avg_c="$avgc";const w_avg=wwhh*avg_c;const iw_avg=wwhh-w_avg;);
 if(!x&&!(y%hh),
  for(v=0,v<length,v++,
   avg_a[v]=sum(crop(#-2,v*ww,y,ww,hh))/iw_avg;
   avg_b[v]=sum(crop(#-3,v*ww,y,ww,hh))/w_avg;
  );
 );
 lerp(avg_a[floor(x/ww)],avg_b[floor(x/ww)],i#1);
 "

So, when x==0 and y%hh is equal to 0, this will execute a loop that is as long as the length. crop function are vectors containing the cropped area of image. Then add all of those vector into a sum. And insert it into a subvector.

Note that #-2 means second last image, v*ww the x-position of crop location, y means the y-position of crop location, and ww,hh is the size of crop.

As for color space conversion, you can do rgb2hsv. RGB255 is generally assuming the default. G’MIC doesn’t really have the concept of color space, so you’d actually have to do some math here.


As for your main question, can you show the before/after image? I believe it can be easily done. Along with information such as dimensions.

Hi Reptorian,

Thanks for your answer. I’ve already started to dive into your code, but this takes some time for me to understand.

Regarding my main question. I have no image currently - will create a sample in GIMP the next days.

Basically I want to enhance the contrast of a “flat/dull” image by stretching the value distribution. A simple approach is to get the min and max values from a gray scale representation of the image and use these values with the cut command. Then I normalize the image to take the full range (0-255). But this does not work as expected. The image has some noise which extents close to 0 and 255. So the min/max are not the real ones, but the min/max of a few noisy pixels. As a result the image is still flat.

A common approach is to skip this noise by cutting all x values below e.g. 0.5% and above 99.5% of the summed up y values. In pseudo-code that means I’ll start at the left side of the histogram, add the height of the left column, then add the next column and so on until I reach the right side. The I’ll calculate 0.5% and 99.5% of this sum and start again summing up. When the sum exceeds the calculated levels I’ll save the x positions in variables and then cut the image. Last step would be to normalize the image to stretch from 0 to 255 (or whatever range I want).

Now, I see what you want.

I think this is what you’re looking for:

rep_normalize_histogram 15%
rep_normalize_histogram:
skip ${2=0},${3=255}
repeat $! l[$>]
 +histogram
 f. i>($1*iM)?1
 hmin={find(crop(#-1),1,0,1)}
 hmax={find(crop(#-1),1,w#-1-1,-1)}
 rm.
 cut. $hmin,$hmax n. $2,$3
endl done
  1. repeat will repeat operations.
  2. $! is the number of images.
  3. lower case l means local to specified images. endl will escape out of local. done means the repeat operation is done.
  4. +histogram creates a histogram.
  5. f. will perform a operation to trim out some histogram. fill is essentially for x,for y.
  6. find will find the “true min”, and “true max”.
  7. cut will limit pixels into these values.
  8. normalize will put the min and max to specified range

Thanks a lot!

I guessed that I can do something with the histogram command - but I had no clue how to do it …

I tried it on an image, but independent of the $1 argument the hmin and hmax result is always -1:

rep_normalize_histogram/*repeat#853/*local#853Compute histogram of image [0], using 256 levels in range [0%,100%].
rep_normalize_histogram/*repeat#853/*local#853/ Fill image [1] with expression ‘i>(15%*iM)?1’.
rep_normalize_histogram/*repeat#853/*local#853/ Set local variable ‘hmin=-1’.
rep_normalize_histogram/*repeat#853/*local#853/ Set local variable ‘hmax=-1’.
rep_normalize_histogram/*repeat#853/*local#853/ Remove image [1] (1 image left).
rep_normalize_histogram/*repeat#853/*local#853/ Cut image [0] in range [-1,-1].
rep_normalize_histogram/*repeat#853/*local#853/ Normalize image [0] in range [0,255], with constant-case ratio 0.75

If I understand the fill command correctly it changes the values of the histogram to 1 if the values are greater than $1 and to 0 if smaller. I think this is not what I need. Instead of “digitizing” the histo I need the total of all histogram values.
Is it possible to loop through the histogram? e.g. something like that: for(x=0;x<255;x++) sum=sum+y(x)

I’ve found the is variable. I guess this is what I need for the total and the calculation of the upper and lower cut border:

sum_i={is#-1}
lower_i={$1*$sum_i}
upper_i={(100%-$1)*$sum_i}

Now how can I iterate through the x-steps of the histogram to add the i values step by step until I reach or exceed the cut borders?

I’m not exactly sure what you want, so I will respond with snip of codes that should lead you to the answer, and my guess as to what you want. My earlier answer is a guess as to what you want based on stretching histogram to normalize values.

If you want to sum=sum+y(x) of a vector. I would do something like.

eval ${-math_lib}"
total_val=0;
for(n=0,n<length,n++,
 temp_val=i(#-1,n,0,0,0);
 total_val=total_val+temp_val;
);
"
output=${}

That was the line I was looking for - thanks a lot!

temp_val={i(#-1,$n,0,0,0)}

Now it works!
Here is my code:

rep_normalize_histogram:
skip ${1=0.5%},${2=0},${3=255}
repeat $! l[$>]
	n=0
	nmax=10000
	-normalize[0] 0,{$nmax}
 	+histogram {$nmax}
 	lower_i={$1*(is#-1)}
 	upper_i={(1-$1)*(is#-1)} 	
	total_val=0
	temp_val=0
	cutmin=0
	cutmax={$nmax}
	min_found=0
	max_found=0	
	do
		temp_val={i(#-1,$n,0,0,0)}
		total_val={$total_val+$temp_val}
		if ($total_val>=$lower_i)&&($min_found==0) cutmin=$n min_found=1 fi
		if ($total_val>=$upper_i)&&($max_found==0) cutmax=$n max_found=1 fi
		n={$n+1}
	while ($n<=$nmax)	
 	-rm[-1] 	
 	-cut[0] {$cutmin},{$cutmax}
 	-normalize[0] $2,$3
endl done

This is a sample image with the default values:

1 Like