Skip to content

Commit

Permalink
Prevent chroma shift during upscales, assuming top-left chroma location
Browse files Browse the repository at this point in the history
  • Loading branch information
erazortt committed Sep 4, 2022
1 parent d5d49f5 commit 0ca321b
Show file tree
Hide file tree
Showing 4 changed files with 233 additions and 23 deletions.
88 changes: 77 additions & 11 deletions DoViBaker/AvisynthEntry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -216,8 +216,85 @@ uint16_t checkNonIdentityMapping(const DoViProcessor& dovi) {
return diffBits;
}

double Spline16Filter(double value) {
value = fabs(value);

if (value < 1.0) {
return ((value - 9.0 / 5.0) * value - 1.0 / 5.0) * value + 1.0;
}
else if (value < 2.0) {
return ((-1.0 / 3.0 * (value - 1.0) + 4.0 / 5.0) * (value - 1.0) - 7.0 / 15.0) * (value - 1.0);
}
return 0.0;
}

double Spline36Filter(double value) {
value = fabs(value);

if (value < 1.0) {
return ((13.0 / 11.0 * (value)-453.0 / 209.0) * (value)-3.0 / 209.0) * (value)+1.0;
}
else if (value < 2.0) {
return ((-6.0 / 11.0 * (value - 1.0) + 270.0 / 209.0) * (value - 1.0) - 156.0 / 209.0) * (value - 1.0);
}
else if (value < 3.0) {
return ((1.0 / 11.0 * (value - 2.0) - 45.0 / 209.0) * (value - 2.0) + 26.0 / 209.0) * (value - 2.0);
}
return 0.0;
}

double Spline64Filter(double value) {
value = fabs(value);

if (value < 1.0) {
return ((49.0 / 41.0 * (value)-6387.0 / 2911.0) * (value)-3.0 / 2911.0) * (value)+1.0;
}
else if (value < 2.0) {
return ((-24.0 / 41.0 * (value - 1.0) + 4032.0 / 2911.0) * (value - 1.0) - 2328.0 / 2911.0) * (value - 1.0);
}
else if (value < 3.0) {
return ((6.0 / 41.0 * (value - 2.0) - 1008.0 / 2911.0) * (value - 2.0) + 582.0 / 2911.0) * (value - 2.0);
}
else if (value < 4.0) {
return ((-1.0 / 41.0 * (value - 3.0) + 168.0 / 2911.0) * (value - 3.0) - 97.0 / 2911.0) * (value - 3.0);
}
return 0.0;
}

int main(int argc, char** argv)
{
/*
printf("Spline64 coefficients\n");
for (int i = 0; i < 4; i++) {
float val = i + 0.5;
printf("%f %i\n", val, int(Spline64Filter(val) * (1 << 12)));
}
printf("Spline16 coefficients\n");
for (int i = 0; i < 2; i++) {
float val = i + 0.5;
printf("%f %i\n", val, int(Spline16Filter(val) * (1 << 12)));
}
printf("Spline36 coefficients\n");
for (int i = -2; i < 4; i++) {
float val = i - 0.25;
printf("%f %i\n", val, int(Spline36Filter(val) * (1 << 8)));
}
printf("Spline16 coefficients\n");
for (int i = -1; i < 3; i++) {
float val = i - 0.25;
printf("%f %i\n", val, int(Spline16Filter(val) * (1 << 7)));
}
printf("Self test\n");
printf("2081 pq = %i\n", DoViProcessor::pq2nits(2081));
for (int i = 0; i <= 100; i += 10) {
std::string out(std::to_string(i));
out += "%: ";
out += std::to_string(DoViProcessor::pq2nits(4095 * i * 0.01));
out += "\n";
printf(out.c_str());
}*/

if (argc < 2) {
printf("DoViAnalyzer: provide path to RPU.bin file\n");
return 1;
Expand All @@ -233,17 +310,6 @@ int main(int argc, char** argv)
fp = fopen(argv[2], "w");
}

/*
printf("Self test\n");
printf("2081 pq = %i\n", DoViProcessor::pq2nits(2081));
for (int i = 0; i <= 100; i += 10) {
std::string out(std::to_string(i));
out += "%: ";
out += std::to_string(DoViProcessor::pq2nits(4095 * i * 0.01));
out += "\n";
printf(out.c_str());
}*/

int length = dovi.getClipLength();
printf("clip length: %i\n", length);
int clip_max_pq = 0;
Expand Down
92 changes: 84 additions & 8 deletions DoViBaker/DoViBaker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,17 +60,17 @@ DoViBaker<quarterResolutionEl>::~DoViBaker()

template<int quarterResolutionEl>
template<int vertLen, int nD>
inline void DoViBaker<quarterResolutionEl>::upsampleVert(PVideoFrame& mez, const PVideoFrame& src, const int plane, const std::array<int, vertLen>& Dn0p, const upscaler_t evenUpscaler, const upscaler_t oddUpscaler, IScriptEnvironment* env)
inline void DoViBaker<quarterResolutionEl>::upsampleVert(PVideoFrame& dst, const PVideoFrame& src, const int plane, const std::array<int, vertLen>& Dn0p, const upscaler_t evenUpscaler, const upscaler_t oddUpscaler, IScriptEnvironment* env)
{
const int srcHeight = src->GetHeight(plane);
const int srcWidth = src->GetRowSize(plane) / sizeof(uint16_t);
const int srcPitch = src->GetPitch(plane) / sizeof(uint16_t);
const uint16_t* srcPb = (const uint16_t*)src->GetReadPtr(plane);

const int dstHeight = mez->GetHeight(plane);
const int dstWidth = mez->GetRowSize(plane) / sizeof(uint16_t);
const int dstPitch = mez->GetPitch(plane) / sizeof(uint16_t);
uint16_t* dstPeven = (uint16_t*)mez->GetWritePtr(plane);
const int dstHeight = dst->GetHeight(plane);
const int dstWidth = dst->GetRowSize(plane) / sizeof(uint16_t);
const int dstPitch = dst->GetPitch(plane) / sizeof(uint16_t);
uint16_t* dstPeven = (uint16_t*)dst->GetWritePtr(plane);
uint16_t* dstPodd = dstPeven + dstPitch;

std::array<const uint16_t*, vertLen> srcP;
Expand Down Expand Up @@ -101,6 +101,53 @@ inline void DoViBaker<quarterResolutionEl>::upsampleVert(PVideoFrame& mez, const
}
}

template<int quarterResolutionEl>
template<int vertLen, int nD>
void DoViBaker<quarterResolutionEl>::upsampleHorz(PVideoFrame& dst, const PVideoFrame& src, const int plane, const std::array<int, vertLen>& Dn0p, const upscaler_t evenUpscaler, const upscaler_t oddUpscaler, IScriptEnvironment* env)
{
const int srcHeight = src->GetHeight(plane);
const int srcWidth = src->GetRowSize(plane) / sizeof(uint16_t);
const int srcPitch = src->GetPitch(plane) / sizeof(uint16_t);
const uint16_t* srcP = (const uint16_t*)src->GetReadPtr(plane);

const int dstHeight = dst->GetHeight(plane);
const int dstWidth = dst->GetRowSize(plane) / sizeof(uint16_t);
const int dstPitch = dst->GetPitch(plane) / sizeof(uint16_t);
uint16_t* dstP = (uint16_t*)dst->GetWritePtr(plane);

static const int pD = vertLen - nD - 1;
std::array<uint16_t, vertLen> value;

for (int h = 0; h < srcHeight; h++) {
for (int w = nD; w < srcWidth - pD; w++) {
dstP[2 * w] = evenUpscaler(&srcP[w - nD], nD);
dstP[2 * w + 1] = oddUpscaler(&srcP[w - nD], nD);
}
for (int w = 0; w < nD; w++) {
for (int i = 0; i < nD; i++) {
int wd = max(w + Dn0p[i], 0);
value[i] = srcP[wd];
}
std::copy_n(&srcP[w], pD + 1, &value[nD]);
dstP[2 * w] = evenUpscaler(&value[0], nD);
dstP[2 * w + 1] = oddUpscaler(&value[0], nD);
}
for (int w = srcWidth - pD; w < srcWidth; w++) {
for (int i = nD + 1; i < Dn0p.size(); i++) {
int wd = min(w + Dn0p[i], srcWidth - 1);
value[i] = srcP[wd];
}
std::copy_n(&srcP[w - nD], nD + 1, &value[0]);
dstP[2 * w] = evenUpscaler(&value[0], nD);
dstP[2 * w + 1] = oddUpscaler(&value[0], nD);
}
srcP += srcPitch;
dstP += dstPitch;
}
}

/*
* these commented out functions use processor functions which were replaced, see DoViProcessor.h
template<int quarterResolutionEl>
void DoViBaker<quarterResolutionEl>::upsampleHorz(PVideoFrame& dst, const PVideoFrame& mez, int plane, IScriptEnvironment* env)
{
Expand Down Expand Up @@ -188,6 +235,35 @@ void DoViBaker<quarterResolutionEl>::upsampleBlChroma(PVideoFrame& dst, const PV
upsampleHorz(dst, mez, PLANAR_U, env);
upsampleHorz(dst, mez, PLANAR_V, env);
}
*/

template<int quarterResolutionEl>
void DoViBaker<quarterResolutionEl>::upscaleEl(PVideoFrame& dst, const PVideoFrame& src, VideoInfo dstVi, IScriptEnvironment* env)
{
dstVi.width /= 2;
PVideoFrame mez = env->NewVideoFrame(dstVi);

upsampleVert<5, 2>(mez, src, PLANAR_Y, { -2,-1, 0, 1, 2 }, &DoViProcessor::upsampleElLumaEven, &DoViProcessor::upsampleElLumaOdd, env);
upsampleVert<4, 1>(mez, src, PLANAR_U, { -1, 0, 1, 2 }, &DoViProcessor::upsampleChromaEven, &DoViProcessor::upsampleChromaOdd, env);
upsampleVert<4, 1>(mez, src, PLANAR_V, { -1, 0, 1, 2 }, &DoViProcessor::upsampleChromaEven, &DoViProcessor::upsampleChromaOdd, env);

upsampleHorz<5, 2>(dst, mez, PLANAR_Y, { -2,-1, 0, 1, 2 }, &DoViProcessor::upsampleElLumaEven, &DoViProcessor::upsampleElLumaOdd, env);
upsampleHorz<4, 1>(dst, mez, PLANAR_U, { -1, 0, 1, 2 }, &DoViProcessor::upsampleChromaEven, &DoViProcessor::upsampleChromaOdd, env);
upsampleHorz<4, 1>(dst, mez, PLANAR_V, { -1, 0, 1, 2 }, &DoViProcessor::upsampleChromaEven, &DoViProcessor::upsampleChromaOdd, env);
}

template<int quarterResolutionEl>
void DoViBaker<quarterResolutionEl>::upsampleChroma(PVideoFrame& dst, const PVideoFrame& src, VideoInfo dstVi, IScriptEnvironment* env)
{
dstVi.width /= 2;
PVideoFrame mez = env->NewVideoFrame(dstVi);

upsampleVert<4, 1>(mez, src, PLANAR_U, { -1, 0, 1, 2 }, &DoViProcessor::upsampleChromaEven, &DoViProcessor::upsampleChromaOdd, env);
upsampleVert<4, 1>(mez, src, PLANAR_V, { -1, 0, 1, 2 }, &DoViProcessor::upsampleChromaEven, &DoViProcessor::upsampleChromaOdd, env);

upsampleHorz<4, 1>(dst, mez, PLANAR_U, { -1, 0, 1, 2 }, &DoViProcessor::upsampleChromaEven, &DoViProcessor::upsampleChromaOdd, env);
upsampleHorz<4, 1>(dst, mez, PLANAR_V, { -1, 0, 1, 2 }, &DoViProcessor::upsampleChromaEven, &DoViProcessor::upsampleChromaOdd, env);
}

template<int quarterResolutionEl>
template<int chromaSubsampling>
Expand Down Expand Up @@ -585,14 +661,14 @@ PVideoFrame DoViBaker<quarterResolutionEl>::GetFrame(int n, IScriptEnvironment*
VideoInfo vi444 = elChild->GetVideoInfo();
vi444.pixel_type = VideoInfo::CS_YUV444P16;
elSrc444 = env->NewVideoFrame(vi444);
upsampleElChroma(elSrc444, elSrc, vi444, env);
upsampleChroma(elSrc444, elSrc, vi444, env);
frameChromaSubSampled = false;
}
if (blClipChromaSubSampled && !elClipChromaSubSampled) {
VideoInfo vi444 = child->GetVideoInfo();
vi444.pixel_type = VideoInfo::CS_YUV444P16;
blSrc444 = env->NewVideoFrame(vi444);
upsampleBlChroma(blSrc444, blSrc, vi444, env);
upsampleChroma(blSrc444, blSrc, vi444, env);
frameChromaSubSampled = false;
}
}
Expand All @@ -609,7 +685,7 @@ PVideoFrame DoViBaker<quarterResolutionEl>::GetFrame(int n, IScriptEnvironment*
VideoInfo vi444 = child->GetVideoInfo();
vi444.pixel_type = VideoInfo::CS_YUV444P16;
mez444 = env->NewVideoFrame(vi444);
upsampleBlChroma(mez444, mez, vi444, env);
upsampleChroma(mez444, mez, vi444, env);
}
convert2rgb(dst, mez, (!mez444)? mez : mez444);
}
Expand Down
9 changes: 6 additions & 3 deletions include/DoViBaker.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@ class DoViBaker : public GenericVideoFilter

private:
void upscaleEl(PVideoFrame& dst, const PVideoFrame& el, VideoInfo dstVi, IScriptEnvironment* env);
void upsampleElChroma(PVideoFrame& dst, const PVideoFrame& el, VideoInfo dstVi, IScriptEnvironment* env);
void upsampleBlChroma(PVideoFrame& dst, const PVideoFrame& el, VideoInfo dstVi, IScriptEnvironment* env);
void upsampleChroma(PVideoFrame& dst, const PVideoFrame& el, VideoInfo dstVi, IScriptEnvironment* env);
//void upsampleElChroma(PVideoFrame& dst, const PVideoFrame& el, VideoInfo dstVi, IScriptEnvironment* env);
//void upsampleBlChroma(PVideoFrame& dst, const PVideoFrame& el, VideoInfo dstVi, IScriptEnvironment* env);

template<int blChromaSubsampling, int elChromaSubsampling>
void doAllQuickAndDirty(PVideoFrame& rgb, const PVideoFrame& blSrc, const PVideoFrame& elSrc, IScriptEnvironment* env) const;
Expand All @@ -43,7 +44,9 @@ class DoViBaker : public GenericVideoFilter
typedef uint16_t(*upscaler_t)(const uint16_t* srcSamples, int idx0);
template<int vertLen, int nD>
void upsampleVert(PVideoFrame& dst, const PVideoFrame& src, int plane, const std::array<int, vertLen>& Dn0p, const upscaler_t evenUpscaler, const upscaler_t oddUpscaler, IScriptEnvironment* env);
void upsampleHorz(PVideoFrame& dst, const PVideoFrame& src, int plane, IScriptEnvironment* env);
template<int vertLen, int nD>
void upsampleHorz(PVideoFrame& dst, const PVideoFrame& src, int plane, const std::array<int, vertLen>& Dn0p, const upscaler_t evenUpscaler, const upscaler_t oddUpscaler, IScriptEnvironment* env);
//void upsampleHorz(PVideoFrame& dst, const PVideoFrame& src, int plane, IScriptEnvironment* env);

PClip elChild;
int CPU_FLAG;
Expand Down
67 changes: 66 additions & 1 deletion include/DoViProcessor.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,19 @@ class DoViProcessor {
inline uint16_t getMaxContentLightLevel() const { return max_content_light_level; }

static inline uint16_t pq2nits(uint16_t pq);


/*
* these upsampling functions are not following the paper, but should be correct when assuming top-left chroma location
*/
static inline constexpr uint16_t upsampleLumaEven(const uint16_t* srcSamples, int idx0);
static inline constexpr uint16_t upsampleLumaOdd(const uint16_t* srcSamples, int idx0);
static inline constexpr uint16_t upsampleChromaEven(const uint16_t* srcSamples, int idx0);
static inline constexpr uint16_t upsampleChromaOdd(const uint16_t* srcSamples, int idx0);
static inline constexpr uint16_t upsampleElLumaEven(const uint16_t* srcSamples, int idx0);
static inline constexpr uint16_t upsampleElLumaOdd(const uint16_t* srcSamples, int idx0);

/*
* these are the original upsampling functions from the paper and seem to assume center-left chroma location which is actually incorrect for hdr sources
static inline constexpr uint16_t upsampleHorzEven(const uint16_t* srcSamples, int idx0);
static inline constexpr uint16_t upsampleHorzOdd(const uint16_t* srcSamples, int idx0);
static inline constexpr uint16_t upsampleBlVertEven(const uint16_t* srcSamples, int idx0);
Expand All @@ -48,6 +60,7 @@ class DoViProcessor {
static inline constexpr uint16_t upsampleElYvertOdd(const uint16_t* srcSamples, int idx0);
static inline constexpr uint16_t upsampleElUVvertEven(const uint16_t* srcSamples, int idx0);
static inline constexpr uint16_t upsampleElUVvertOdd(const uint16_t* srcSamples, int idx0);
*/

inline uint16_t processSampleY(uint16_t bl, uint16_t el) const;
inline uint16_t processSampleU(uint16_t bl, uint16_t el, uint16_t mmrBlY, uint16_t mmrBlU, uint16_t mmrBlV) const;
Expand Down Expand Up @@ -138,38 +151,89 @@ constexpr uint16_t DoViProcessor::Clip3(uint16_t lower, uint16_t upper, int valu
return max(min(value, upper), lower);
}

/*
* these upsampling functions are not following the paper, but should be correct when assuming top-left chroma location
*/
constexpr uint16_t DoViProcessor::upsampleLumaEven(const uint16_t* y, int n)
{
// this matches quite exactly spline36 at positions -2.75, -1.75, -0.75, 0.25, 1.25, 2.25
auto val = (2 * y[n - 3] - 12 * y[n - 2] + 65 * y[n - 1] + 222 * y[n] - 25 * y[n + 1] + 4 * y[n + 2] + 128) >> 8;
return Clip3(0, 0xFFFF, val);
}

constexpr uint16_t DoViProcessor::upsampleLumaOdd(const uint16_t* y, int n)
{
// this matches quite exactly spline36 at positions -2.25, -1.25, -0.25, 0.75, 1.75, 2.75
auto val = (4 * y[n - 2] - 25 * y[n - 1] + 222 * y[n] + 65 * y[n + 1] - 12 * y[n + 2] + 2 * y[n + 3] + 128) >> 8;
return Clip3(0, 0xFFFF, val);
}

constexpr uint16_t DoViProcessor::upsampleChromaEven(const uint16_t* y, int n)
{
return y[n];
}

constexpr uint16_t DoViProcessor::upsampleChromaOdd(const uint16_t* y, int n)
{
// this is spline16 at positions 0.5, 1.5
auto val = (-307 * y[n - 1] + 2355 * y[n] + 2355 * y[n + 1] - 307 * y[n + 2] + 2048) >> 12;
return Clip3(0, 0xFFFF, val);
}

constexpr uint16_t DoViProcessor::upsampleElLumaEven(const uint16_t* y, int n)
{
// this matches quite exactly spline16 at positions -1.75, -0.75, 0.25, 1.25
auto val = (-3 * y[n - 2] + 29 * y[n - 1] + 111 * y[n] - 9 * y[n + 1] + 64) >> 7;
return Clip3(0, 0xFFFF, val);
}

constexpr uint16_t DoViProcessor::upsampleElLumaOdd(const uint16_t* y, int n)
{
// this matches quite exactly spline16 at positions -1.25, -0.25, 0.75, 1.75
auto val = (-9 * y[n - 1] + 111 * y[n] + 29 * y[n + 1] - 3 * y[n + 2] + 64) >> 7;
return Clip3(0, 0xFFFF, val);
}

/*
* these are the original upsampling functions from the paper and seem to assume center-left chroma location which is actually incorrect for hdr sources
constexpr uint16_t DoViProcessor::upsampleHorzEven(const uint16_t* y, int n)
{
return y[n];
}
constexpr uint16_t DoViProcessor::upsampleHorzOdd(const uint16_t* y, int n)
{
// this matches quite exactly spline64 at positions 0.5, 1.5, 2.5, 3.5
auto val = (22 * y[n - 3] + 94 * y[n - 2] - 524 * y[n - 1] + 2456 * y[n] + 2456 * y[n + 1] - 524 * y[n + 2] +
94 * y[n + 3] + 22 * y[n + 4] + 2048) >> 12;
return Clip3(0, 0xFFFF, val);
}
constexpr uint16_t DoViProcessor::upsampleBlVertEven(const uint16_t* y, int n)
{
// this matches quite exactly spline36 at positions -2.75, -1.75, -0.75, 0.25, 1.25, 2.25
auto val = (2 * y[n - 3] - 12 * y[n - 2] + 65 * y[n - 1] + 222 * y[n] - 25 * y[n + 1] + 4 * y[n + 2] + 128) >> 8;
return Clip3(0, 0xFFFF, val);
}
constexpr uint16_t DoViProcessor::upsampleBlVertOdd(const uint16_t* y, int n)
{
// this matches quite exactly spline36 at positions -2.25, -1.25, -0.25, 0.75, 1.75, 2.75
auto val = (4 * y[n - 2] - 25 * y[n - 1] + 222 * y[n] + 65 * y[n + 1] - 12 * y[n + 2] + 2 * y[n + 3] + 128) >> 8;
return Clip3(0, 0xFFFF, val);
}
constexpr uint16_t DoViProcessor::upsampleElYvertEven(const uint16_t* y, int n)
{
// this matches quite exactly spline16 at positions -1.75, -0.75, 0.25, 1.25
auto val = (-3 * y[n - 2] + 29 * y[n - 1] + 111 * y[n] - 9 * y[n + 1] + 64) >> 7;
return Clip3(0, 0xFFFF, val);
}
constexpr uint16_t DoViProcessor::upsampleElYvertOdd(const uint16_t* y, int n)
{
// this matches quite exactly spline16 at positions -1.25, -0.25, 0.75, 1.75
auto val = (-9 * y[n - 1] + 111 * y[n] + 29 * y[n + 1] - 3 * y[n + 2] + 64) >> 7;
return Clip3(0, 0xFFFF, val);
}
Expand All @@ -185,6 +249,7 @@ constexpr uint16_t DoViProcessor::upsampleElUVvertOdd(const uint16_t* y, int n)
auto val = (192 * y[n] + 64 * y[n + 1] + 128) >> 8;
return min(0xFFFF, val);
}
*/

uint16_t DoViProcessor::processSampleY(uint16_t bl, uint16_t el) const {
return processSample(0, bl, el, 0, 0, 0);
Expand Down

0 comments on commit 0ca321b

Please sign in to comment.