Welcome, Guest.
Username: Password: Remember me

TOPIC: HQX and Median Filters

HQX and Median Filters 1 year 6 months ago #1

  • tybalitea
  • tybalitea's Avatar
  • Offline
  • Posts: 28
  • Thank you received: 17
Hi i was wondering if it was possible to add these kind of filters
i think it would be useful and interesting for image treatment...

good examples there :

hqx : www.shadertoy.com/view/MslGRS
median fiter : www.shadertoy.com/view/XsXGDX
The administrator has disabled public write access.

HQX and Median Filters 1 year 6 months ago #2

  • Martigen
  • Martigen's Avatar
  • Offline
  • Posts: 144
  • Thank you received: 35
tybalitea wrote:
Hi i was wondering if it was possible to add these kind of filters
i think it would be useful and interesting for image treatment...

good examples there :

hqx : www.shadertoy.com/view/MslGRS
median fiter : www.shadertoy.com/view/XsXGDX
That HQ4X is pretty awesome. I was playing Endless Dungeon recently and trying to find ways with the Framework to improve the pixel art (they went total hardcore and it's as pixelated as that Nyan cat, too much for the wonderful design imho) -- and this would be the perfect shader for it!

How hard is it to add a shader like this? I'd happily test it and post screenies of Endless Dungeon with it!
The administrator has disabled public write access.

HQX and Median Filters 1 year 6 months ago #3

  • crosire
  • crosire's Avatar
  • Offline
  • Posts: 2438
  • Thank you received: 1385
Totally untested, but should work. It's ready to be pluged into the framework like usual (copy into "ReShade\CustomFX\HQ4X.h" and add "#include EFFECT(CustomFX, HQ4X)" to ReShade.fx):
// hq4x filter
// Ripped from https://github.com/libretro/common-shaders/blob/master/hqx/hq4x.cg

float4 PS_HQ4X(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
	float mx = 1.0; // start smoothing wt.
	const float k = -1.10; // wt. decrease factor
	const float max_w = 0.75; // max filter weigth
	const float min_w = 0.03; // min filter weigth
	const float lum_add = 0.33; // effects smoothing

	float4 color = tex2D(RFX_backbufferColor, uv);
	float3 c = color.xyz;

	float x = 0.5 * (1.0 / 256.0);
	float y = 0.5 * (1.0 / 32.0);

	const float3 dt = 1.0*float3(1.0, 1.0, 1.0);

	float2 dg1 = float2( x, y);
	float2 dg2 = float2(-x, y);

	float2 sd1 = dg1*0.5;
	float2 sd2 = dg2*0.5;

	float2 ddx = float2(x,0.0);
	float2 ddy = float2(0.0,y);

	float4 t1 = float4(uv-sd1,uv-ddy);
	float4 t2 = float4(uv-sd2,uv+ddx);
	float4 t3 = float4(uv+sd1,uv+ddy);
	float4 t4 = float4(uv+sd2,uv-ddx);
	float4 t5 = float4(uv-dg1,uv-dg2);
	float4 t6 = float4(uv+dg1,uv+dg2);

	float3 i1 = tex2D(RFX_backbufferColor, t1.xy).xyz;
	float3 i2 = tex2D(RFX_backbufferColor, t2.xy).xyz;
	float3 i3 = tex2D(RFX_backbufferColor, t3.xy).xyz;
	float3 i4 = tex2D(RFX_backbufferColor, t4.xy).xyz;

	float3 o1 = tex2D(RFX_backbufferColor, t5.xy).xyz;
	float3 o3 = tex2D(RFX_backbufferColor, t6.xy).xyz;
	float3 o2 = tex2D(RFX_backbufferColor, t5.zw).xyz;
	float3 o4 = tex2D(RFX_backbufferColor, t6.zw).xyz;

	float3 s1 = tex2D(RFX_backbufferColor, t1.zw).xyz;
	float3 s2 = tex2D(RFX_backbufferColor, t2.zw).xyz;
	float3 s3 = tex2D(RFX_backbufferColor, t3.zw).xyz;
	float3 s4 = tex2D(RFX_backbufferColor, t4.zw).xyz;

	float ko1 = dot(abs(o1-c),dt);
	float ko2 = dot(abs(o2-c),dt);
	float ko3 = dot(abs(o3-c),dt);
	float ko4 = dot(abs(o4-c),dt);

	float k1=min(dot(abs(i1-i3),dt),max(ko1,ko3));
	float k2=min(dot(abs(i2-i4),dt),max(ko2,ko4));

	float w1 = k2; if(ko3<ko1) w1*=ko3/ko1;
	float w2 = k1; if(ko4<ko2) w2*=ko4/ko2;
	float w3 = k2; if(ko1<ko3) w3*=ko1/ko3;
	float w4 = k1; if(ko2<ko4) w4*=ko2/ko4;

	c=(w1*o1+w2*o2+w3*o3+w4*o4+0.001*c)/(w1+w2+w3+w4+0.001);
	w1 = k*dot(abs(i1-c)+abs(i3-c),dt)/(0.125*dot(i1+i3,dt)+lum_add);
	w2 = k*dot(abs(i2-c)+abs(i4-c),dt)/(0.125*dot(i2+i4,dt)+lum_add);
	w3 = k*dot(abs(s1-c)+abs(s3-c),dt)/(0.125*dot(s1+s3,dt)+lum_add);
	w4 = k*dot(abs(s2-c)+abs(s4-c),dt)/(0.125*dot(s2+s4,dt)+lum_add);

	w1 = clamp(w1+mx,min_w,max_w);
	w2 = clamp(w2+mx,min_w,max_w);
	w3 = clamp(w3+mx,min_w,max_w);
	w4 = clamp(w4+mx,min_w,max_w);

	return float4((w1*(i1+i3)+w2*(i2+i4)+w3*(s1+s3)+w4*(s2+s4)+c)/(2.0*(w1+w2+w3+w4)+1.0), 1.0);
}

technique HQ4X_Tech < enabled = RFX_Start_Enabled; >
{
	pass
	{
		VertexShader = RFX_VS_PostProcess;
		PixelShader = PS_HQ4X;
	}
}
Cheers, crosire =)
The administrator has disabled public write access.
The following user(s) said Thank You: tybalitea, jas01

HQX and Median Filters 1 year 6 months ago #4

  • Martigen
  • Martigen's Avatar
  • Offline
  • Posts: 144
  • Thank you received: 35
crosire wrote:
Totally untested, but should work. It's ready to be pluged into the framework like usual (copy into "ReShade\CustomFX\HQ4X.h" and add "#include EFFECT(CustomFX, HQ4X)" to ReShade.fx):
Omigosh awsome! Heading off to bed so will try this out tomorrow :) Thankyou!
The administrator has disabled public write access.

HQX and Median Filters 1 year 5 months ago #5

  • Martigen
  • Martigen's Avatar
  • Offline
  • Posts: 144
  • Thank you received: 35
Finally got around to trying this, thanks Crosire.

It's... almost working? :) The effect looks like french window blinds horizontally across the screen. Something is working, but not quite right yet. I don't want to distract you from Reshade though so don't stress. I also realised, looking at the window-blinded interface result, that of course the interface will be affected unless using a mask, and it's one of those games in which the interface is always changing, so it might not be practical to use (for this game, at least) in the long run. Still a cool effect though.
Last Edit: 1 year 5 months ago by Martigen.
The administrator has disabled public write access.

HQX and Median Filters 1 year 5 months ago #6

HQ4X
edit the x y to output res in corsair's conversion.
	float x = 0.5 * (1.0 / Width);
	float y = 0.5 * (1.0 / Hight);
Last Edit: 1 year 5 months ago by kingeric1992.
The administrator has disabled public write access.
The following user(s) said Thank You: tybalitea

HQX and Median Filters 1 year 5 months ago #7

  • tybalitea
  • tybalitea's Avatar
  • Offline
  • Posts: 28
  • Thank you received: 17
seems to work quite well, here are some tests (my screen res is 1920*1080):

vanilla :


float x = 0.5 * (1.0 / 640.0);
float y = 0.5 * (1.0 / 360.0);





float x = 0.5 * (1.0 / 1024.0);
float y = 0.5 * (1.0 / 576.0);




float x = 1.5 * (1.0 / 1920.0);
float y = 1.5 * (1.0 / 1080.0);


The administrator has disabled public write access.

HQX and Median Filters 1 year 5 months ago #8

  • crosire
  • crosire's Avatar
  • Offline
  • Posts: 2438
  • Thank you received: 1385
I suggest to just replace it with:
float x = 0.5 * BUFFER_RCP_WIDTH;
float y = 0.5 * BUFFER_RCP_HEIGHT;
That'll work with every resolution =).
Cheers, crosire =)
Last Edit: 1 year 5 months ago by crosire.
The administrator has disabled public write access.

HQX and Median Filters 1 year 5 months ago #9

  • tybalitea
  • tybalitea's Avatar
  • Offline
  • Posts: 28
  • Thank you received: 17
yes much better indeed, changing the multiplier should be enough to define the strength
The administrator has disabled public write access.

HQX and Median Filters 1 year 5 months ago #10

  • Marty McFly
  • Marty McFly's Avatar
  • Offline
  • We've tried nothing and we're all out of ideas!
  • Posts: 609
  • Thank you received: 716
I actually ported a more complex median filter version as experiment for blurring of noisy raw SSAO, might as well port it as standalone shader.
The administrator has disabled public write access.
The following user(s) said Thank You: tybalitea, jas01

HQX and Median Filters 1 year 5 months ago #11

  • Martigen
  • Martigen's Avatar
  • Offline
  • Posts: 144
  • Thank you received: 35
As promised, pics from Dungeon of the Endless! (open full-screen in separate tabs to compare)

HQ4X off (2560x1600):


HQ4X on y = 0.5 * (1.0 / 2560); float y = 0.5 * (1.0 / 1600);


HQ4X on y = 0.5 * (1.0 / 1000); float y = 0.5 * (1.0 / 500);


The pipe and the green sludge in the middle top... almost no pixelation! Impressive. Of course, it's also blurring the interface for now.

Edited for Crosire's change and playing around with scales. Interesting way to soften an image (besides FXAA blurring) for higher-res sources I imagine.
Last Edit: 1 year 5 months ago by Martigen.
The administrator has disabled public write access.
The following user(s) said Thank You: tybalitea

HQX and Median Filters 1 year 3 months ago #12

Can you guys port xBRZ filter to reshade please? It's something similar to hqx,but is a more advanced one.
Link below:
sourceforge.net/projects/xbrz/?source=navbar
Last Edit: 1 year 3 months ago by acknowledge.
The administrator has disabled public write access.

HQX and Median Filters 1 year 3 months ago #13

  • Martigen
  • Martigen's Avatar
  • Offline
  • Posts: 144
  • Thank you received: 35
Just a bump -- can we get this added officially to the Framework? @luciferhawk
The administrator has disabled public write access.

HQX and Median Filters 1 year 2 months ago #14

  • Ganossa
  • Ganossa's Avatar
  • Offline
  • 최정장군
  • Posts: 790
  • Thank you received: 838
Done :P

The administrator has disabled public write access.
The following user(s) said Thank You: SunBroDave, acknowledge

HQX and Median Filters 1 year 2 months ago #15

  • ninjafada
  • ninjafada's Avatar
  • Offline
  • Posts: 38
  • Thank you received: 5
will you add this to the next framework package update ?
The administrator has disabled public write access.

HQX and Median Filters 1 year 1 month ago #16

  • Violins77
  • Violins77's Avatar
  • Offline
  • Posts: 10
  • Thank you received: 1
LuciferHawk wrote:
Done :P


Guys! I am DYING to tet this effect! I've been trying for hours to get it to work, but I just see that it should be added to the framework! Is it gonna be released soon?
The administrator has disabled public write access.

HQX and Median Filters 1 year 1 month ago #17

  • ninjafada
  • ninjafada's Avatar
  • Offline
  • Posts: 38
  • Thank you received: 5
i made a zip with the file ninjafada.com/sweetfx/HQ4X.zip , extract in reshade folder, edit CustomFX.cfg (end of the file ) to change the strength

Last Edit: 1 year 1 month ago by ninjafada.
The administrator has disabled public write access.

HQX and Median Filters 10 months 1 week ago #18

  • JPulowski
  • JPulowski's Avatar
  • Offline
  • Posts: 92
  • Thank you received: 86
This is not the true HQ4X filter. The shader has been ported from Shadertoy and it is dated as 2013-07-12. It was based on an older version of HQ4X from RetroArch. On June 3, 2014 it has been replaced with Armada's true HQX shaders. This is the reason why the code on GitHub and Shadertoy looks different. You can find more about it here: filthypants.blogspot.com/2014/06/true-hq2x-shader-comparison-with-xbr.html

Currently I am trying to port "true" version, but since scaling is involved in the process it is quite complex for me to port everything at first try. But eventually it will be done, I guess.
The administrator has disabled public write access.
The following user(s) said Thank You: Quentin-Tarantino, Genrix, mcabel

HQX and Median Filters 10 months 4 days ago #19

Hi, is there a configuration I can Use to make HQ4x look like typically expected?



I'm using it on final fantasy tactics -remotejoy on my pc, and instead of looking smooth and sharp, the image looks blur-ier like if it had gaussian blur instead:

http://ibin.co/2VDcm8OvxGbY
above-unfiltered / below "hq4x" .

Also +1 to xBR ....it's like hq4x but the image output tend to be more accurate and nicer looking.
Last Edit: 10 months 4 days ago by mcabel. Reason: typo
The administrator has disabled public write access.

HQX and Median Filters 10 months 3 days ago #20

@JPulowski
Untested, consider it pseudo code.
I've comb though Armada651's port to the C version, looks quite legit so it's pretty much a direct port.
Didn't check the LUT texture though, (would be a pain in the ass) but you can still procedurally generate the LUT tex on Reshade initialization if you don't want to use Armada651's pregenerated luttex.
/**************************************************************************
 * Copyright (C) 2003 Maxim Stepin (  )    // HQnX
 * Copyright (C) 2010 Cameron Zemek ( ) // HQnX
 * Copyright (C) 2011 Francois Gannaz <>  // HQnX
 * Copyright (C) 2014 Jules Blok (  )         // Armada651
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 **************************************************************************
 *
 * HQnX Initinal port on ReShade by kingeric1992 .Feb/1/16
 *
 * check 
 *      https://code.google.com/archive/p/hqx/                      for original C implementation
 *      https://github.com/libretro/common-shaders/tree/master/hqx  for Cg/HLSL implementation by Armada651
 *
 **************************************************************************/
 
//settings
#define HQnX_ToggleKey (unbound)    //HQnX toggle key
#define HQnX_Scale 4                //set scale 2|3|4
#define HQnX_InputTex InputTexture  //set input texture
#define HQnX_SrcScaleX  640         //source scale X
#define HQnX_SrcScaleY  480         //source scale Y
float3  HQnX_YUVoffset    = {0, 0.5, 0.5};            //or use define
float3  HQnX_YUVthreshold = {0.1882, 0.0275, 0.0235}; //or use define

//textures & samplers.
//lut tex: .rgba == {center weight, diagonal weight, horizontal weight, vertical weight}
texture HQ4xTex < source = "ReShade/HQ4xLut.png"; > { Width = 256; Height = 256; Format = RGBA8F; };
texture HQ3xTex < source = "ReShade/HQ3xLut.png"; > { Width = 256; Height = 144; Format = RGBA8F; };
texture HQ2xTex < source = "ReShade/HQ2xLut.png"; > { Width = 256; Height = 64;  Format = RGBA8F; };

sampler HQ4xLutColor { Texture = HQ4xTex;       MinFilter = POINT; MagFilter = POINT;};
sampler HQ3xLutColor { Texture = HQ3xTex;       MinFilter = POINT; MagFilter = POINT;};
sampler HQ2xLutColor { Texture = HQ2xTex;       MinFilter = POINT; MagFilter = POINT;};
sampler HQnXSrcColor { Texture = HQnX_InputTex; MinFilter = POINT; MagFilter = POINT;};

#define HQnXLutColor(a) HQ##a##xLutColor

float3x3 yuv =
{
    0.299,  0.587,  0.114,
   -0.169, -0.331,  0.5,
      0.5, -0.419, -0.081
};

bool Diff(float3 yuv1, float3 yuv2) {
    bool3 res = abs((yuv1 + HQnX_YUVoffset) - (yuv2 + HQnX_YUVoffset)) > HQnX_YUVthreshold;
    return res.x || res.y || res.z;
}

struct VS_OUTPUT {
    float4 position  : POSITION;
    float4 texCoord0 : TEXCOORD0;
    float4 texCoord1 : TEXCOORD1;
    float4 texCoord2 : TEXCOORD2;
};

VS_OUTPUT VS_HQnX(in uint id : SV_VertexID)
{
    VS_OUTPUT OUT;

    float2 coord = 0;
    coord.x = (id == 2) ? 2.0 : 0.0;
    coord.y = (id == 1) ? 2.0 : 0.0;

    float4 offset = {- 1 / HQnX_SrcScaleX, 0, 1 / HQnX_SrcScaleX, 1 / HQnX_SrcScaleY}; //  Src Pixel Size
    OUT.position  = float4(coord * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0);
    OUT.texCoord0 = coord.xxxy + offset.xyzw;
    OUT.texCoord1 = coord.xxxy + offset.xyzy;
    OUT.texCoord2 = coord.xxxy - offset.zyxw;
    return OUT;
}

//*  use point sampler
//** only support upscale x2, x3, x4.
float4 PS_HQnX(in VS_OUTPUT IN) : COLOR
{
    float4   offset = {- 1 / HQnX_SrcScaleX, 0, 1 / HQnX_SrcScaleX, 1 / HQnX_SrcScaleY}; //  Src Pixel Size
    float2   index  = frac(IN.texCoord / offset.zw);
    float2   dir    = sign(index - 0.5);
    float4x3 pixels = //4 rows, 3 column
    {
        tex2D(HQnXSrcColor, IN.texCoord1.yw                  ).rgb, //center
        tex2D(HQnXSrcColor, IN.texCoord1.yw + dir * offset.zw).rgb, //diagonal
        tex2D(HQnXSrcColor, IN.texCoord1.yw + dir * offset.zy).rgb, //horizontal
        tex2D(HQnXSrcColor, IN.texCoord1.yw + dir * offset.yw).rgb  //vertical
    };

    float3 w[9];                                               //   +------+------+------+
    w[1] = mul(yuv, tex2D(HQnXSrcColor, IN.texCoord0.xw).rgb); //   |      |      |      |
    w[2] = mul(yuv, tex2D(HQnXSrcColor, IN.texCoord0.yw).rgb); //   | w[1] | w[2] | w[3] |
    w[3] = mul(yuv, tex2D(HQnXSrcColor, IN.texCoord0.zw).rgb); //   +------+------+------+
    w[4] = mul(yuv, tex2D(HQnXSrcColor, IN.texCoord1.xw).rgb); //   |      |      |      |
    w[0] = mul(yuv, pixels[0]);                                //   | w[4] | w[0] | w[5] |
    w[5] = mul(yuv, tex2D(HQnXSrcColor, IN.texCoord1.zw).rgb); //   +------+------+------+
    w[6] = mul(yuv, tex2D(HQnXSrcColor, IN.texCoord2.xw).rgb); //   |      |      |      |
    w[7] = mul(yuv, tex2D(HQnXSrcColor, IN.texCoord2.yw).rgb); //   | w[6] | w[7] | w[8] |
    w[8] = mul(yuv, tex2D(HQnXSrcColor, IN.texCoord2.zw).rgb); //   +------+------+------+

    //use LUT 256 cases * (16 if else * (n * n) bias) to replace switch table, index to get lut offset
    //check https://code.google.com/archive/p/hqx/ cpu source on switch table.
    index    = floor(index * HQnX_Scale);
    index.y  = index.x + index.y * HQnX_Scale;
    index.y += (Diff(w[4], w[2]) + (Diff(w[2], w[5]) << 1) + (Diff(w[7], w[4]) << 2) + (Diff(w[5], w[7]) << 3)) * (HQnX_Scale * HQnX_Scale);
    index.x  = Diff(w[0], w[1]);

    for(int i=1; i<8; i++)
        index.x += Diff(w[0], w[i+1]) << i;

    index   = (index + 0.5) / float2(256.0, 16.0 * (HQnX_Scale * HQnX_Scale));

//or use linear sampling to colapse sample quad (bypass float4x3 pixels) similar to optimized bicubic filter
/*
    float4 weight = tex2D(HQnXLutColor(HQnX_Scale), index);
    index.y = (weight.y + weight.w ) / dot(weight, 1);
    index.x = lerp( weight.z / (weight.x + weight.z), weight.w / (weight.y + weight.w), index.y);
    return tex2D(decal, (floor(IN.texCoord / offset.zw) + 0.5 + index * dir) * offset.zw));
*/
    float4 weight = tex2D(HQnXLutColor(HQnX_Scale), index);
    return float4(mul(weight / dot(weight, 1), pixels), 1.0);
}

technique HQnX < enabled = RFX_Start_Enabled; toggle = HQnX_ToggleKey; >
{
	pass 	{
		VertexShader = VS_HQnX;
		PixelShader  = PS_HQnX;
	}
}
Last Edit: 10 months 3 days ago by kingeric1992. Reason: typo
The administrator has disabled public write access.
The following user(s) said Thank You: JPulowski