Sign In

crabshank

User Group
Members
Join date
18-Oct-2018
Last activity
7-Sep-2019
Posts
3

Post History

Post
#1250165
Topic
Curves (based on G'MIC) and HSV dither hlsl
Time

An example (using the shader posted):

http://www.framecompare.com/screenshotcomparison/WYD7NNNX

I also have an image remapping page that does histogram matching for selected channels. It also presents all the options to white balance (just give it an image containing areas that are supposed to be greyscale)

https://github.com/crabshank/HLSL-Reshade-colour-remappers/blob/master/Image histogram.html

Post
#1250040
Topic
Curves (based on G'MIC) and HSV dither hlsl
Time

Since I thought that the Curves function in G’MIC (GIMP) was very good for colour mapping, I wanted to use it for video in some way. So I created this code that lets you remap RGB and HSV values. It just does straight line interpolation, not curves.

I also have a dithering shader that allows you to increase the mean or standard deviation of the image by adding (pseudo-)random amounts according to a uniform distribution (almost), I suggest using this to deblock or deband. You can also use my crushing filter here to clean up the image afterwards.

sampler s0 : register(s0);
float4 p0 :  register(c0);
float4 p1 :  register(c1);

#define width   (p0[0])
#define height  (p0[1])
#define counter (p0[2])
#define clock   (p0[3])
#define one_over_width  (p1[0])
#define one_over_height (p1[1])


float3 rgb2hsv(float3 c)
{
    float4 K = float4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
    float4 p = lerp(float4(c.bg, K.wz), float4(c.gb, K.xy), step(c.b, c.g));
    float4 q = lerp(float4(p.xyw, c.r), float4(c.r, p.yzx), step(p.x, c.r));
 
    float d = q.x - min(q.w, q.y);
    float e = 1.0e-10;
    return float3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
}


float3 hsv2rgb(float3 c)
{
    float4 K = float4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
    float3 p = abs(frac(c.xxx + K.xyz) * 6.0 - K.www);
    return c.z * lerp(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}
//Source: http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl

//Bounds for interpolation//
static float r_x_b[2]={0,1};static float r_y_b[2]={0,1};static float g_x_b[2]={0,1};static float g_y_b[2]={0,1};static float b_x_b[2]={0,1};static float b_y_b[2]={0,1};static float h_x_b[2]={0,1};static float h_y_b[2]={0,1};static float s_x_b[2]={0,1};static float s_y_b[2]={0,1};static float v_x_b[2]={0,1};static float v_y_b[2]={0,1};

#define red_on 0
#define red_points 3

static float2 r[red_points] = {
0, 0,    // row 1; x,y
128,128,
255,255
};

#define green_on 0
#define green_points 3

static float2 g[green_points] = {
0, 0,    // row 1; x,y
128,128,
255,255
};

#define blue_on 0
#define blue_points 3

static float2 b[blue_points] = {
0, 0,    // row 1; x,y
128,128,
255,255
};


static float rotate_hues = 0; //-360 to 360

#define hue_on 1
#define hue_points 7

static float2 h[hue_points] = {
0, 0,    // row 1; x,y
10,10,
120,128,
185,183,
294,302,
302,310,
360,360
};

#define sat_on 1
#define sat_range 100
#define sat_points 3

static float2 s[sat_points] = {
0, 0,    // row 1; x,y
69,80,
100,100
};

#define val_on 1
#define val_range 100
#define val_points 4

static float2 v[val_points]= {
0, 0,    // row 1; x,y
35,34,
77,84,
100,100
};

#define split 1
#define flip_split 0
#define split_position  0.5

#define noWorseWB 0

float red_map(float red){int i=0;int exact=0;[unroll(red_points)]for(i=0;i<red_points;i++){[branch]if(r[i][0]/255==red) {red=r[i][1]/255;exact=1;i=red_points-1;}else{if(r[i][0]/255<red&&r[i][0]/255>=r_x_b[0]){r_x_b[0]=r[i][0]/255;r_y_b[0]=r[i][1]/255;} if(r[i][0]/255<=r_x_b[1]&&red<r[i][0]/255){r_x_b[1]=r[i][0]/255;r_y_b[1]=r[i][1]/255;}}} if(exact==0){red=r_y_b[0]+(red-r_x_b[0])*((r_y_b[1]-r_y_b[0])/(r_x_b[1]-r_x_b[0]));}return red;} 

float green_map(float green){int i=0;int exact=0;[unroll(green_points)]for(i=0;i<green_points;i++){[branch]if(g[i][0]/255==green) {green=g[i][1]/255;exact=1;i=green_points-1;}else{if(g[i][0]/255<green&&g[i][0]/255>=g_x_b[0]){g_x_b[0]=g[i][0]/255;g_y_b[0]=g[i][1]/255;} if(g[i][0]/255<=g_x_b[1]&&green<g[i][0]/255){g_x_b[1]=g[i][0]/255;g_y_b[1]=g[i][1]/255;}}} if(exact==0){green=g_y_b[0]+(green-g_x_b[0])*((g_y_b[1]-g_y_b[0])/(g_x_b[1]-g_x_b[0]));}return green;} 

float blue_map(float blue){int i=0;int exact=0;[unroll(blue_points)]for(i=0;i<blue_points;i++){[branch]if(b[i][0]/255==blue) {blue=b[i][1]/255;exact=1;i=blue_points-1;}else{if(b[i][0]/255<blue&&b[i][0]/255>=b_x_b[0]){b_x_b[0]=b[i][0]/255;b_y_b[0]=b[i][1]/255;} if(b[i][0]/255<=b_x_b[1]&&blue<b[i][0]/255){b_x_b[1]=b[i][0]/255;b_y_b[1]=b[i][1]/255;}}} if(exact==0){blue=b_y_b[0]+(blue-b_x_b[0])*((b_y_b[1]-b_y_b[0])/(b_x_b[1]-b_x_b[0]));}return blue;} 

float hue_rotate(float hue, float deg){deg*=pow(360,-1); float r=hue+deg; float r1=(r<=1)?r:r-1; hue=(r<0)?1+r:r1; return hue;}

float hue_map(float hue){int i=0;int exact=0;[unroll(hue_points)]for(i=0;i<hue_points;i++){[branch]if(h[i][0]/360==hue) {hue=h[i][1]/360;exact=1;i=hue_points-1;}else{if(h[i][0]/360<hue&&h[i][0]/360>=h_x_b[0]){h_x_b[0]=h[i][0]/360;h_y_b[0]=h[i][1]/360;} if(h[i][0]/360<=h_x_b[1]&&hue<h[i][0]/360){h_x_b[1]=h[i][0]/360;h_y_b[1]=h[i][1]/360;}}} if(exact==0){hue=h_y_b[0]+(hue-h_x_b[0])*((h_y_b[1]-h_y_b[0])/(h_x_b[1]-h_x_b[0]));}return hue;} 

float sat_map(float sat){int i=0;int exact=0;[unroll(sat_points)]for(i=0;i<sat_points;i++){[branch]if(s[i][0]/sat_range==sat) {sat=s[i][1]/sat_range;exact=1;i=sat_points-1;}else{if(s[i][0]/sat_range<sat&&s[i][0]/sat_range>=s_x_b[0]){s_x_b[0]=s[i][0]/sat_range;s_y_b[0]=s[i][1]/sat_range;} if(s[i][0]/sat_range<=s_x_b[1]&&sat<s[i][0]/sat_range){s_x_b[1]=s[i][0]/sat_range;s_y_b[1]=s[i][1]/sat_range;}}} if(exact==0){sat=s_y_b[0]+(sat-s_x_b[0])*((s_y_b[1]-s_y_b[0])/(s_x_b[1]-s_x_b[0]));}return sat;} 

float val_map(float val){int i=0;int exact=0;[unroll(val_points)]for(i=0;i<val_points;i++){[branch]if(v[i][0]/val_range==val) {val=v[i][1]/val_range;exact=1;i=val_points-1;}else{if(v[i][0]/val_range<val&&v[i][0]/val_range>=v_x_b[0]){v_x_b[0]=v[i][0]/val_range;v_y_b[0]=v[i][1]/val_range;} if(v[i][0]/val_range<=v_x_b[1]&&val<v[i][0]/val_range){v_x_b[1]=v[i][0]/val_range;v_y_b[1]=v[i][1]/val_range;}}} if(exact==0){val=v_y_b[0]+(val-v_x_b[0])*((v_y_b[1]-v_y_b[0])/(v_x_b[1]-v_x_b[0]));}return val;}

float4 remapper(float4 c0){
float3 orig=c0.rgb;
[branch]if(red_on==1){c0.r=red_map(c0.r);}		//red
[branch]if(green_on==1){c0.g=green_map(c0.g);}	//green
[branch]if(blue_on==1){c0.b=blue_map(c0.b);}	//blue

[branch]if (noWorseWB==1){

float3 origHSV=rgb2hsv(orig.rgb);
float3 newHSV=rgb2hsv(c0.rgb);
float origMin=min(orig.r,min(orig.g,orig.b));
float newMin=min(c0.r,min(c0.g,c0.b));

float ogChoice=origMin*(1-origHSV.y)+pow(1-origMin,2);
float nwChoice=newMin*(1-newHSV.y)+pow(1-newMin,2);

c0.rgb=(ogChoice>nwChoice)?orig.rgb:c0.rgb;
}

float3 colorHSV= rgb2hsv(c0.rgb);

[branch]if(rotate_hues!=0){
	colorHSV.x=hue_rotate(colorHSV.x,rotate_hues);
	c0.rgb=hsv2rgb(colorHSV); colorHSV= rgb2hsv(c0.rgb); //hue rotate
}

[branch]if(hue_on==1){
colorHSV.x=hue_map(colorHSV.x);
c0.rgb=hsv2rgb(colorHSV); 
colorHSV= rgb2hsv(c0.rgb); //hue
	}

[branch]if(sat_on==1){
colorHSV.y=sat_map(colorHSV.y); 
c0.rgb=hsv2rgb(colorHSV); 
colorHSV= rgb2hsv(c0.rgb); //sat
}

[branch]if(val_on==1){
colorHSV.z=val_map(colorHSV.z);
c0.rgb=hsv2rgb(colorHSV);
colorHSV= rgb2hsv(c0.rgb); //val
}

	return c0;


}


float4 main(float2 tex : TEXCOORD0) : COLOR {

float4 c0=tex2D(s0, tex);

float Split=split;
float Split_position=split_position;
float Flip_split=flip_split;

float4 c1=remapper(c0);

float4 c2=(tex.x>=Split_position*Split)?c1:c0;
float4 c3=(tex.x<=Split_position*Split)?c1:c0;

float4 c4=(Flip_split*Split==1)?c3:c2;

float divLine = abs(tex.x - Split_position) < one_over_width;
c4 =(Split==0)?c4: c4*(1.0 - divLine); //invert divline

return c4;
}

Here’s the grey dither:

sampler s0 : register(s0);
float4 p0 :  register(c0);
float4 p1 :  register(c1);

#define width   (p0[0])
#define height  (p0[1])
#define counter (p0[2])
#define clock   (p0[3])
#define one_over_width  (p1[0])
#define one_over_height (p1[1])

static float d_x_b[2]={0,1};static float d_y_b[2]={0,1};
 
#define dither_points 7

static float2 d[dither_points] = {
0,0,
1,0,
64.90915,63.75,
131.4898,127.5,
194.1036,191.25,
254,255,
255,255
};

float dither_map(float dither){int i=0;int exact=0;[unroll(dither_points)]for(i=0;i<dither_points;i++){[branch]if(d[i][0]/255==dither) {dither=d[i][1]/255;exact=1;i=dither_points-1;}else{if(d[i][0]/255<dither&&d[i][0]/255>=d_x_b[0]){d_x_b[0]=d[i][0]/255;d_y_b[0]=d[i][1]/255;} if(d[i][0]/255<=d_x_b[1]&&dither<d[i][0]/255){d_x_b[1]=d[i][0]/255;d_y_b[1]=d[i][1]/255;}}} if(exact==0){dither=d_y_b[0]+(dither-d_x_b[0])*((d_y_b[1]-d_y_b[0])/(d_x_b[1]-d_x_b[0]));}return dither;} 


float random( float2 p )
{
// We need irrationals for pseudo randomness.
// Most (all?) known transcendental numbers will (generally) work.
const float2 r = float2(
23.1406926327792690,  // e^pi (Gelfond's constant)
 2.6651441426902251); // 2^sqrt(2) (Gelfond-Schneider constant)

float t=frac(acos(p.x/p.y)+sin(p.x)*r.y+cos(p.y)*r.x+p.x*p.y*r.y);

t= frac((800*cos(t/20)+1400)*t);  
t= frac(pow( frac((0.01*t+sin(500*t*t))+tan(t*500)*500),2));

float rMap =3.98;
float tOld=t;
int i=0;

[unroll(100)]for (i=0;i<100;i++){
tOld=rMap*tOld*(1-tOld);
}
 
 float w = frac(10000*tOld+0.597*tOld);


return dither_map(w);

}

float grey_dither(float color,float2 tex,float rnd,float sdv, float gamma){

float rand=random(float2((tex.x+width*one_over_height)*color,(tex.y+height*one_over_width)*color));
float randm=rnd*-1*((rand*-4)+1); // averages to color + rnd

color=(rnd!=0)?color+(randm/255):color;

float sAB=sdv*sqrt(12)*0.5;
randm=sAB*(2*rand-1)*-1;
color=(sdv!=0)?color+(randm/255):color;

float colorSc=color*2;
randm=(colorSc<0.5)?(pow(abs(0.5*colorSc),gamma)-color)*-1*((rand*-4)+1):((1-(0.5*pow(abs(2-colorSc),gamma)))-color)*-1*((rand*-4)+1);

color=(gamma!=1)?color+randm:color;

return color;

}

#define greyDither_Amnt 0

#define greyDither_Sdv 0

#define greyDither_Scurve 1

#define dark_Dither 1 //0 or 1

#define dark_Dither_Pwr 0 //0-1



#define split 0

#define flip_split 0

#define split_position 0.5


float4 main(float2 tex : TEXCOORD0) : COLOR 
{ 
	float4 c0 = tex2D(s0, tex);
	float c0Max=max(c0.r,max(c0.g,c0.b));
	float4 c1 = c0;
	float Split=split;
	float Split_position=split_position;
	float Flip_split=flip_split;
	float greyDitherScurve=greyDither_Scurve;
	float greyDitherSdv=greyDither_Sdv;
	float greyDitherAmnt=greyDither_Amnt;
	float Dark_dither=dark_Dither;
	float Dark_dither_pwr=dark_Dither_Pwr;

c1.rgb =saturate(grey_dither(c0Max,tex,greyDitherAmnt,greyDitherSdv,greyDitherScurve)*(c1.rgb/c0Max));

	float c1Max=max(c1.r,max(c1.g,c1.b));

c1.rgb=(Dark_dither==1)?lerp(c1.rgb,c0.rgb,pow(c1Max,1-Dark_dither_pwr)):c1.rgb;

float4 c2=(tex.x>=Split_position*Split)?c1:c0;
float4 c3=(tex.x<=Split_position*Split)?c1:c0;

float4 c4=(Flip_split*Split==1)?c3:c2;

float divLine = abs(tex.x - Split_position) < one_over_width;
c4 =(Split==0)?c4: c4*(1.0 - divLine); //invert divline

return c4;

}