HDR -> SDR conversion

These CUDA filters are packaged into DGDecodeNV, which is part of DGDecNV.
Selur
Distinguished Member
Distinguished Member
Posts: 103
Joined: Mon Nov 05, 2012 4:49 pm

Re: HDR -> SDR tonemapping

Post by Selur » Sun Apr 01, 2018 6:41 am

Small note about nvhsp: It only patches the first header and is only meant for the output of nvencc, but is not really needed nowadays since nvencc itself supports hdr related signaling.

@admin: Are you planning to port DGTonemap to Vapoursynth? (I'd like a gpu based alternative to tonemap)

Cu Selur

gonca
Distinguished Member
Distinguished Member
Posts: 583
Joined: Sun Apr 08, 2012 6:12 pm

Re: HDR -> SDR tonemapping

Post by gonca » Sun Apr 01, 2018 6:48 am

NVEncC does accept the HDR signalling through the command line since a few versions ago.
Both work, so they are both good, depending on workflow

User avatar
admin
Site Admin
Posts: 3927
Joined: Thu Sep 09, 2010 3:08 pm

Re: HDR -> SDR tonemapping

Post by admin » Sun Apr 01, 2018 7:40 am

Thanks for the useful info, guys.
Selur wrote:
Sun Apr 01, 2018 6:41 am
Are you planning to port DGTonemap to Vapoursynth? (I'd like a gpu based alternative to tonemap)
Yes, I will. And I'm depackaging filters so they can be open-sourced. Only DGSource() will remain closed.

Nginx
Posts: 23
Joined: Fri Mar 23, 2018 12:48 am

Re: HDR -> SDR tonemapping

Post by Nginx » Sun Apr 01, 2018 10:21 pm

admin wrote:
Sun Apr 01, 2018 7:40 am
Thanks for the useful info, guys.
Selur wrote:
Sun Apr 01, 2018 6:41 am
Are you planning to port DGTonemap to Vapoursynth? (I'd like a gpu based alternative to tonemap)
Yes, I will. And I'm depackaging filters so they can be open-sourced. Only DGSource() will remain closed.
Sounds real great. :hat:

User avatar
admin
Site Admin
Posts: 3927
Joined: Thu Sep 09, 2010 3:08 pm

Re: HDR -> SDR tonemapping

Post by admin » Mon Apr 02, 2018 11:00 am

Ready for a splash of ice water in the face, guys? I already took one. :(

Here's the deal. A CUDA version of DGTonemap is currently useless, for two reasons: a) the overhead of transferring three (R, G, B) float images to/from the GPU makes the CUDA version perform worse than simply doing things in SW with a decent prefetch, and b) the overall time is heavily dominated by the SW conversions, so that even if the tonemapping part performed better with CUDA, it would have little overall effect.

So, what is the morale of this story? Everything has to be done on the GPU, i.e, ship up one 16-bit frame, convert to 709 and tonemap, and ship back the frame. Then we could expect a large speedup. Got my work cut out for me. :scratch:

gonca
Distinguished Member
Distinguished Member
Posts: 583
Joined: Sun Apr 08, 2012 6:12 pm

Re: HDR -> SDR tonemapping

Post by gonca » Mon Apr 02, 2018 11:20 am

Ready for a splash of ice water in the face, guys? I already took one. :(
Don't go swimming in Lake Erie yet ;)
As for the rest, if anyone can do it you can
Don't forget your paper reviews, or to relax every so often.
The software version already gives us a good solution

User avatar
admin
Site Admin
Posts: 3927
Joined: Thu Sep 09, 2010 3:08 pm

Re: HDR -> SDR tonemapping

Post by admin » Mon Apr 02, 2018 11:52 am

Reviews are done, but I'm going to take your advice to relax. :lol:

User avatar
hydra3333
Distinguished Aussie Member
Distinguished Aussie Member
Posts: 118
Joined: Wed Oct 06, 2010 3:34 am
Contact:

Re: HDR -> SDR tonemapping

Post by hydra3333 » Wed Apr 04, 2018 4:54 am

8-)

dmcs
Posts: 32
Joined: Sat Oct 21, 2017 9:40 pm

Re: HDR -> SDR tonemapping

Post by dmcs » Fri Apr 13, 2018 4:08 pm

I have a question. In the DGTonemap's manual, the formula for Hable is

Code: Select all

hable(x) = ((x*(a*x+c*b)+d*e) / (x*(a*x+b)+d*f)) - e/f
Can you tell me what the "x" variable means? Thank you.

User avatar
admin
Site Admin
Posts: 3927
Joined: Thu Sep 09, 2010 3:08 pm

Re: HDR -> SDR tonemapping

Post by admin » Fri Apr 13, 2018 4:19 pm

It's easiest just to give you the code.

Code: Select all

#include "windows.h"
#include "avisynth.h"
#include "stdio.h"

class DGReinhard : public GenericVideoFilter
{
	float contrast;
	float bright;

public:
	DGReinhard(PClip _child, float _contrast, float _bright, IScriptEnvironment* env) : GenericVideoFilter(_child)
	{
		if (vi.pixel_type != VideoInfo::CS_RGBPS)
		{
			env->ThrowError("DGReinhard: input must be CS_RGBPS");
		}
		contrast = _contrast;
		bright = _bright;
	}
    PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env);
};

PVideoFrame __stdcall DGReinhard::GetFrame(int n, IScriptEnvironment* env)
{
    PVideoFrame src = child->GetFrame(n, env);
	PVideoFrame dst = env->NewVideoFrame(vi);

	float *srcpR, *srcpG, *srcpB;
	float *dstpR, *dstpG, *dstpB;

	const int src_pitch = src->GetPitch(PLANAR_R) / 4;
	const int dst_pitch = dst->GetPitch(PLANAR_R) / 4;
	const int row_size = dst->GetRowSize(PLANAR_R);
	const int width = vi.width;
	const int height = dst->GetHeight(PLANAR_R);

	const float offset = (1.0f - contrast) / contrast;
	const float factor = (bright + offset) / bright;

	srcpR = (float *)src->GetReadPtr(PLANAR_R);
	dstpR = (float *)dst->GetWritePtr(PLANAR_R);
	srcpG = (float *)src->GetReadPtr(PLANAR_G);
	dstpG = (float *)dst->GetWritePtr(PLANAR_G);
	srcpB = (float *)src->GetReadPtr(PLANAR_B);
	dstpB = (float *)dst->GetWritePtr(PLANAR_B);
	for (int y = 0; y < height; y++)
	{
		for (int x = 0; x < width; x++)
		{
			dstpR[x] = srcpR[x] / (srcpR[x] + offset) * factor;
			dstpG[x] = srcpG[x] / (srcpG[x] + offset) * factor;
			dstpB[x] = srcpB[x] / (srcpB[x] + offset) * factor;
		}
		srcpR += src_pitch;
		srcpG += src_pitch;
		srcpB += src_pitch;
		dstpR += dst_pitch;
		dstpG += dst_pitch;
		dstpB += dst_pitch;
	}

	return dst;
}

class DGHable : public GenericVideoFilter
{
	float exposure, a, b, c, d, e, f, w;

public:
	DGHable(PClip _child, float _exposure,
		float _a,
		float _b,
		float _c,
		float _d,
		float _e,
		float _f,
		float _w,
		IScriptEnvironment* env) : GenericVideoFilter(_child)
	{
		if (vi.pixel_type != VideoInfo::CS_RGBPS)
		{
			env->ThrowError("DGHable: input must be CS_RGBPS");
		}
		exposure = _exposure;
		a = _a;
		b = _b;
		c = _c;
		d = _d;
		e = _e;
		f = _f;
		w = _w;
	}

	float hable(float in)
	{
		return (in * (in * a + b * c) + d * e) / (in * (in * a + b) + d * f) - e / f;
	}

	PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env);
};

PVideoFrame __stdcall DGHable::GetFrame(int n, IScriptEnvironment* env)
{
	PVideoFrame src = child->GetFrame(n, env);
	PVideoFrame dst = env->NewVideoFrame(vi);

	float *srcpR, *srcpG, *srcpB;
	float *dstpR, *dstpG, *dstpB;

	const int src_pitch = src->GetPitch(PLANAR_R) / 4;
	const int dst_pitch = dst->GetPitch(PLANAR_R) / 4;
	const int row_size = dst->GetRowSize(PLANAR_R);
	const int width = vi.width;
	const int height = dst->GetHeight(PLANAR_R);

	srcpR = (float *)src->GetReadPtr(PLANAR_R);
	dstpR = (float *)dst->GetWritePtr(PLANAR_R);
	srcpG = (float *)src->GetReadPtr(PLANAR_G);
	dstpG = (float *)dst->GetWritePtr(PLANAR_G);
	srcpB = (float *)src->GetReadPtr(PLANAR_B);
	dstpB = (float *)dst->GetWritePtr(PLANAR_B);

	float whitescale = 1.0f / hable(w);

	for (int y = 0; y < height; y++)
	{
		for (int x = 0; x < width; x++)
		{
			dstpR[x] = hable(exposure * srcpR[x]) * whitescale;
			dstpG[x] = hable(exposure * srcpG[x]) * whitescale;
			dstpB[x] = hable(exposure * srcpB[x]) * whitescale;
		}
		srcpR += src_pitch;
		srcpG += src_pitch;
		srcpB += src_pitch;
		dstpR += dst_pitch;
		dstpG += dst_pitch;
		dstpB += dst_pitch;
	}

	return dst;
}

AVSValue __cdecl Create_DGReinhard(AVSValue args, void* user_data, IScriptEnvironment* env)
{
	return new DGReinhard(args[0].AsClip(),
		(float)args[1].AsFloat(0.3f),
		(float)args[2].AsFloat(5.0f),
		env);
}

AVSValue __cdecl Create_DGHable(AVSValue args, void* user_data, IScriptEnvironment* env)
{
	return new DGHable(args[0].AsClip(),
		(float)args[1].AsFloat(2.0f),
		(float)args[2].AsFloat(0.15f),
		(float)args[3].AsFloat(0.50f),
		(float)args[4].AsFloat(0.10f),
		(float)args[5].AsFloat(0.20f),
		(float)args[6].AsFloat(0.02f),
		(float)args[7].AsFloat(0.30f),
		(float)args[8].AsFloat(11.20f),
		env);
}

const AVS_Linkage *AVS_linkage = 0;
extern "C" __declspec(dllexport) const char* __stdcall AvisynthPluginInit3(IScriptEnvironment* env, AVS_Linkage* vectors)
{
	AVS_linkage = vectors;
	env->AddFunction("DGReinhard", "c[contrast]f[bright]f", Create_DGReinhard, 0);
	env->AddFunction("DGHable", "c[exposure]f[a]f[b]f[c]f[d]f[e]f[f]f[w]f", Create_DGHable, 0);
	return 0;
}

Post Reply