Skip to content

Instantly share code, notes, and snippets.

@dwbuiten
Last active July 8, 2020 13:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dwbuiten/2a9b37cb64842b88d507f45d4b08d17c to your computer and use it in GitHub Desktop.
Save dwbuiten/2a9b37cb64842b88d507f45d4b08d17c to your computer and use it in GitHub Desktop.
#!/usr/bin/env perl
use strict;
use warnings;
use utf8;
use bignum;
use Statistics::Basic qw(mean stddev);
use List::Util qw(max);
my @id;
my @yuv;
my @ycgco;
my @haar;
my $list = shift;
for (`cat $list`) { # lazy
chomp;
open(my $f, "<", "$_") or die("cant open $_");
my $i = 0;
while (<$f>) {
chomp;
if ($i == 0) {
$i++;
next;
}
my @vals = split(',');
if (scalar(@vals) != 5) {
die("invalid number of vals");
}
push(@id, ($vals[1] / $vals[0]) * 100);
push(@yuv, ($vals[2] / $vals[0]) * 100);
push(@ycgco, ($vals[3] / $vals[0]) * 100);
push(@haar, ($vals[4] / $vals[0]) * 100);
$i++;
}
if ($i != 2) {
die("$_ is invalid");
}
close($f);
}
my $name = (split(/\./, $list))[0];
printf("|-----------------------------------------------------------|\n");
printf("| $name".(" " x (58 - length($name)))."|\n");
printf("|-----------------------------------------------------------|\n");
printf("| Identity (GBR) | Mean: %6.2f%% | StdDev: %6.02f%% |\n", mean(\@id), stddev(\@id));
printf("| YCbCr 10-bit | Mean: %6.2f%% | StdDev: %6.02f%% |\n", mean(\@yuv), stddev(\@yuv));
printf("| YCgCo 9-bit (as 10-bit) | Mean: %6.2f%% | StdDev: %6.02f%% |\n", mean(\@ycgco), stddev(\@ycgco));
printf("| PLHaar N-bit to N-bit | Mean: %6.2f%% | StdDev: %6.02f%% |\n", mean(\@haar), stddev(\@haar));
printf("|-----------------------------------------------------------|\n");
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <assert.h>
static void plhaar_int(int *l, int *h, int a, int b, int c)
{
const int s = (a < c), t = (b < c);
a += s; b += t;
if (s == t) {
a -= b - c;
if ((a < c) == s)
b += a - c;
} else {
b += a - c;
if ((b < c) == t)
a -= b - c;
}
a -= s; b -= t;
*l = b; *h = a;
}
static void haar_conv(uint8_t *dst, uint8_t *src, int w, int h)
{
uint8_t *g = src;
uint8_t *b = g + w * h;
uint8_t *r = b + w * h;
uint8_t *yp = dst;
uint8_t *u = yp + w * h;
uint8_t *v = u + w * h;
int x, y;
for (y = 0; y < h; y++) {
for (x = 0; x < w; x++) {
int ri = (int) r[y * w + x];
int gi = (int) g[y * w + x];
int bi = (int) b[y * w + x];
int sum, diff;
plhaar_int(&sum, &diff, ri, bi, 128);
int ui = diff;
int tmp = sum;
plhaar_int(&sum, &diff, gi, tmp, 128);
int vi = diff;
int ypi = sum;
assert(ypi <= 255 && ypi >= 0);
assert(ui <= 255 && ui >= 0);
assert(vi <= 255 && vi >= 0);
yp[y * w + x] = (uint8_t) ypi;
u[y * w + x] = (uint8_t) ui;
v[y * w + x] = (uint8_t) vi;
/* Everything below here is non-functional paranoid checks. */
plhaar_int(&sum, &diff, ypi, vi, 128);
int gicheck = sum;
tmp = diff;
plhaar_int(&sum, &diff, tmp, ui, 128);
int richeck = sum;
int bicheck = diff;
assert(ri == richeck);
assert(gi == gicheck);
assert(bi == bicheck);
}
}
}
static void ycgco_r_conv(uint8_t *dst, uint8_t *src, int w, int h)
{
uint8_t *g = src;
uint8_t *b = g + w * h;
uint8_t *r = b + w * h;
uint8_t *yp = dst;
uint8_t *cg = yp + w * h * 2;
uint8_t *co = cg + w * h * 2;
int x, y;
for (y = 0; y < h; y++) {
int ox;
for (x = 0, ox = 0; x < w; x++, ox += 2) {
int16_t g16 = g[y * w + x];
int16_t b16 = b[y * w + x];
int16_t r16 = r[y * w + x];
int16_t co16 = r16 - b16;
int16_t tmp16 = b16 + (co16 / 2);
int16_t cg16 = g16 - tmp16;
int16_t y16 = tmp16 + (cg16 / 2);
co16 += 256;
cg16 += 256;
/* Little endian. */
yp[y * (w * 2) + ox + 0] = (uint8_t) (((uint16_t) y16) & 0xFF);
yp[y * (w * 2) + ox + 1] = (uint8_t) ((((uint16_t) y16) & 0xFF00) >> 8);
cg[y * (w * 2) + ox + 0] = (uint8_t) (((uint16_t) cg16) & 0xFF);
cg[y * (w * 2) + ox + 1] = (uint8_t) ((((uint16_t) cg16) & 0xFF00) >> 8);
co[y * (w * 2) + ox + 0] = (uint8_t) (((uint16_t) co16) & 0xFF);
co[y * (w * 2) + ox + 1] = (uint8_t) ((((uint16_t) co16) & 0xFF00) >> 8);
/* Everything below here is non-functional paranoid checks. */
assert(y16 <= 255 && y16 >= 0);
assert(cg16 <= 511 && y16 >= 0);
assert(co16 <= 511 && y16 >= 0);
co16 -= 256;
cg16 -= 256;
tmp16 = y16 - (cg16 / 2);
int16_t g16check = cg16 + tmp16;
int16_t b16check = tmp16 - (co16 / 2);
int16_t r16check = b16check + co16;
assert(g16check == g16);
assert(b16check == b16);
assert(r16check == r16);
}
}
}
int main(int argc, char *argv[])
{
int ret = 0;
uint8_t *inbuf = NULL, *ycgcobuf = NULL, *haarbuf = NULL;
FILE *in = NULL, *yout = NULL, *hout = NULL;
int width, height;
size_t fret;
if (argc < 6) {
printf("Usage: conv file.gbrp width height ycgco-r.out haar.out\n");
return 1;
}
width = atoi(argv[2]);
height = atoi(argv[3]);
if (width <= 0 || height <= 0) {
printf("Invalid width/height (%dx%d).\n", width, height);
return 1;
}
in = fopen(argv[1], "rb");
if (in == NULL) {
printf("Failed to open %s\n", argv[1]);
return 1;
}
inbuf = malloc(width * height * 3);
if (inbuf == NULL) {
printf("Failed to allocate input buffer.\n");
ret = 1;
goto end;
}
fret = fread(inbuf, 1, width * height * 3, in);
if (fret != (size_t) width * height * 3) {
printf("Failed to read enough input bytes (wanted %zu got %d).\n", fret, width * height * 3);
ret = 1;
goto end;
}
fclose(in);
in = NULL;
ycgcobuf = malloc(width * height * 3 * 2);
if (ycgcobuf == NULL) {
printf("Failed to allocate YCgCo buffer.\n");
ret = 1;
goto end;
}
ycgco_r_conv(ycgcobuf, inbuf, width, height);
yout = fopen(argv[4], "wb");
if (yout == NULL) {
printf("Failed to open out.ycgco\n");
ret = 1;
goto end;
}
fret = fwrite(ycgcobuf, 1, width * height * 3 * 2, yout);
if (fret != (size_t) width * height * 3 * 2) {
printf("Failed to write enough bytes (wanted %zu got %d).\n", fret, width * height * 3 * 2);
ret = 1;
goto end;
}
fclose(yout);
yout = NULL;
free(ycgcobuf);
ycgcobuf = NULL;
haarbuf = malloc(width * height * 3);
if (haarbuf == NULL) {
printf("Failed to allocate Haar buffer.\n");
ret = 1;
goto end;
}
haar_conv(haarbuf, inbuf, width, height);
free(inbuf);
inbuf = NULL;
hout = fopen(argv[5], "wb");
if (hout == NULL) {
printf("Failed to open out.haar.\n");
ret = 1;
goto end;
}
fret = fwrite(haarbuf, 1, width * height * 3, hout);
if (fret != (size_t) width * height * 3) {
printf("Failed to write enough bytes (wanted %zu got %d).\n", fret, width * height * 3);
ret = 1;
goto end;
}
fclose(hout);
hout = NULL;
free(haarbuf);
haarbuf = NULL;
end:
if (inbuf != NULL)
free(inbuf);
if (ycgcobuf != NULL)
free(ycgcobuf);
if (haarbuf != NULL)
free(haarbuf);
if (in != NULL)
fclose(in);
if (yout != NULL)
fclose(yout);
if (hout != NULL)
fclose(hout);
return ret;
}
#!/bin/sh -e
FILE=$1
DIR=$(dirname ${FILE})
TMP=$(ffprobe -show_streams "${FILE}" 2>/dev/null | egrep "^width=|^height=" | tr '\n' ' ')
WIDTH=$(echo ${TMP} | sed -e 's/width=\(.\+\) height=.\+/\1/')
HEIGHT=$(echo ${TMP} | sed -e 's/width=.\+ height=\(.\+\)/\1/')
ffmpeg -i "${FILE}" -pix_fmt gbrp -f rawvideo -y "${FILE}.gbrp" 2>/dev/null
conv "${FILE}".gbrp ${WIDTH} ${HEIGHT} "${FILE}.ycgco" "${FILE}.haar"
ffmpeg -f rawvideo -s ${WIDTH}x${HEIGHT} -pix_fmt gbrp -i "${FILE}.gbrp" -y "${FILE}.png" 2>/dev/null
pngcrush -brute "${FILE}.png" "${FILE}.crush.png" 2>/dev/null
avifenc --lossless --range full --yuv 444 --depth 10 "${FILE}.png" -s 0 "${FILE}.ycbcr.avif" >/dev/null
ffmpeg -f rawvideo -s ${WIDTH}x${HEIGHT} -color_range pc -pix_fmt yuv444p -i "${FILE}.gbrp" -color_range pc -strict -1 -y "${FILE}.identity.y4m" 2>/dev/null
avifenc --lossless "${FILE}.identity.y4m" -s 0 "${FILE}.identity.avif" >/dev/null
ffmpeg -f rawvideo -s ${WIDTH}x${HEIGHT} -color_range pc -pix_fmt yuv444p10le -i "${FILE}.ycgco" -color_range pc -strict -1 -y "${FILE}.ycgco.y4m" 2>/dev/null
avifenc --lossless "${FILE}.ycgco.y4m" -s 0 "${FILE}.ycgco.avif" >/dev/null
ffmpeg -f rawvideo -s ${WIDTH}x${HEIGHT} -color_range pc -pix_fmt yuv444p -i "${FILE}.haar" -color_range pc -strict -1 -y "${FILE}.haar.y4m" 2>/dev/null
avifenc --lossless "${FILE}.haar.y4m" -s 0 "${FILE}.haar.avif" >/dev/null
PNGSIZE=$(stat -c '%s' "${FILE}.crush.png")
IDSIZE=$(stat -c '%s' "${FILE}.identity.avif")
YUVSIZE=$(stat -c '%s' "${FILE}.ycbcr.avif")
YCGCOSIZE=$(stat -c '%s' "${FILE}.ycgco.avif")
HAARSIZE=$(stat -c '%s' "${FILE}.haar.avif")
rm "${FILE}.png"
rm "${FILE}.crush.png"
rm "${FILE}.ycbcr.avif"
rm "${FILE}.gbrp"
rm "${FILE}.identity.y4m"
rm "${FILE}.identity.avif"
rm "${FILE}.ycgco"
rm "${FILE}.ycgco.y4m"
rm "${FILE}.ycgco.avif"
rm "${FILE}.haar"
rm "${FILE}.haar.y4m"
rm "${FILE}.haar.avif"
echo "PNG,IDENTITY,YCBCR,YCGCO,HAAR" > ${FILE}.csv
echo "${PNGSIZE},${IDSIZE},${YUVSIZE},${YCGCOSIZE},${HAARSIZE}" >> ${FILE}.csv
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment