blob: f16205a4b96638fbc8820a707491c4ff059a0d4d [file] [log] [blame]
/*
* Copyright (c) 2022 Samsung Electronics Co., Ltd.
* All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* - Neither the name of the copyright owner, nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _OAPV_APP_UTIL_H_
#define _OAPV_APP_UTIL_H_
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <assert.h>
#include <math.h>
#include <stdarg.h>
#include <ctype.h>
#if LINUX
#include <signal.h>
#include <unistd.h>
#endif
#define VERBOSE_NONE 0
#define VERBOSE_ERROR 1
#define VERBOSE_SIMPLE 2
#define VERBOSE_FRAME 3
/* logging functions */
static void log_msg(char *filename, int line, const char *fmt, ...)
{
char str[1024] = { '\0' };
if(filename != NULL && line >= 0)
sprintf(str, "[%s:%d] ", filename, line);
va_list args;
va_start(args, fmt);
vsprintf(str + strlen(str), fmt, args);
va_end(args);
printf("%s", str);
}
static void log_line(char *pre)
{
int i, len;
char str[128] = { '\0' };
const int chars = 80;
for(i = 0; i < 3; i++) {
str[i] = '=';
}
str[i] = '\0';
len = (pre == NULL) ? 0 : (int)strlen(pre);
if(len > 0) {
sprintf(str + 3, " %s ", pre);
len = (int)strlen(str);
}
for(i = len; i < chars; i++) {
str[i] = '=';
}
str[chars] = '\0';
printf("%s\n", str);
}
#if defined(__GNUC__)
#define __FILENAME__ \
(strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
#define logerr(args...) \
{ \
if(op_verbose >= VERBOSE_ERROR) { \
log_msg(NULL, -1, args); \
} \
}
#define logv2(args...) \
{ \
if(op_verbose >= VERBOSE_SIMPLE) { \
log_msg(NULL, -1, args); \
} \
}
#define logv3(args...) \
{ \
if(op_verbose >= VERBOSE_FRAME) { \
log_msg(NULL, -1, args); \
} \
}
#else
#define __FILENAME__ \
(strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__)
#define logerr(args, ...) \
{ \
if(op_verbose >= VERBOSE_ERROR) { \
log_msg(NULL, -1, args, __VA_ARGS__); \
} \
}
#define logv2(args, ...) \
{ \
if(op_verbose >= VERBOSE_SIMPLE) { \
log_msg(NULL, -1, args, __VA_ARGS__); \
} \
}
#define logv3(args, ...) \
{ \
if(op_verbose >= VERBOSE_FRAME) { \
log_msg(NULL, -1, args, __VA_ARGS__); \
} \
}
#endif
#define logv2_line(pre) \
{ \
if(op_verbose >= VERBOSE_SIMPLE) { \
log_line(pre); \
} \
}
#define logv3_line(pre) \
{ \
if(op_verbose >= VERBOSE_FRAME) { \
log_line(pre); \
} \
}
/* assert function */
#include <assert.h>
#define assert_r(x) \
{ \
if(!(x)) { \
assert(x); \
return; \
} \
}
#define assert_rv(x, r) \
{ \
if(!(x)) { \
assert(x); \
return (r); \
} \
}
#define assert_g(x, g) \
{ \
if(!(x)) { \
assert(x); \
goto g; \
} \
}
#define assert_gv(x, r, v, g) \
{ \
if(!(x)) { \
assert(x); \
(r) = (v); \
goto g; \
} \
}
static int op_verbose = VERBOSE_SIMPLE;
/* Clocks */
#if defined(_WIN64) || defined(_WIN32)
#include <windows.h>
typedef DWORD oapv_clk_t;
#define OAPV_CLK_PER_SEC (1000)
#define OAPV_CLK_PER_MSEC (1)
#define OAPV_CLK_MAX ((oapv_clk_t)(-1))
#define oapv_clk_get() GetTickCount()
#elif __linux__ || __CYGWIN__ || __APPLE__
#include <time.h>
#include <sys/time.h>
typedef unsigned long oapv_clk_t;
#define OAPV_CLK_MAX ((oapv_clk_t)(-1))
#define OAPV_CLK_PER_SEC (10000)
#define OAPV_CLK_PER_MSEC (10)
static oapv_clk_t oapv_clk_get(void)
{
oapv_clk_t clk;
struct timeval t;
gettimeofday(&t, NULL);
clk = t.tv_sec * 10000L + t.tv_usec / 100L;
return clk;
}
#else
#error THIS PLATFORM CANNOT SUPPORT CLOCK
#endif
static __inline oapv_clk_t oapv_clk_diff(oapv_clk_t t1, oapv_clk_t t2)
{
return (((t2) >= (t1)) ? ((t2) - (t1)) : ((OAPV_CLK_MAX - (t1)) + (t2)));
}
static __inline oapv_clk_t oapv_clk_from(oapv_clk_t from)
{
oapv_clk_t now = oapv_clk_get();
return oapv_clk_diff(from, now);
}
static __inline oapv_clk_t oapv_clk_msec(oapv_clk_t clk)
{
return ((oapv_clk_t)((clk + (OAPV_CLK_PER_MSEC / 2)) / OAPV_CLK_PER_MSEC));
}
static __inline oapv_clk_t oapv_clk_sec(oapv_clk_t clk)
{
return ((oapv_clk_t)((clk + (OAPV_CLK_PER_SEC / 2)) / OAPV_CLK_PER_SEC));
}
#define CLIP_VAL(n, min, max) (((n) > (max)) ? (max) : (((n) < (min)) ? (min) : (n)))
#define ALIGN_VAL(val, align) ((((val) + (align) - 1) / (align)) * (align))
/* Function for atomic increament:
This function might need to modify according to O/S or CPU platform
*/
static int atomic_inc(volatile int *pcnt)
{
int ret;
ret = *pcnt;
ret++;
*pcnt = ret;
return ret;
}
/* Function for atomic decrement:
This function might need to modify according to O/S or CPU platform
*/
static int atomic_dec(volatile int *pcnt)
{
int ret;
ret = *pcnt;
ret--;
*pcnt = ret;
return ret;
}
/* Function to allocate memory for picture buffer:
This function might need to modify according to O/S or CPU platform
*/
static void *picbuf_alloc(int size)
{
return malloc(size);
}
/* Function to free memory allocated for picture buffer:
This function might need to modify according to O/S or CPU platform
*/
static void picbuf_free(void *p)
{
if(p) {
free(p);
}
}
static int imgb_addref(oapv_imgb_t *imgb)
{
assert_rv(imgb, OAPV_ERR_INVALID_ARGUMENT);
return atomic_inc(&imgb->refcnt);
}
static int imgb_getref(oapv_imgb_t *imgb)
{
assert_rv(imgb, OAPV_ERR_INVALID_ARGUMENT);
return imgb->refcnt;
}
static int imgb_release(oapv_imgb_t *imgb)
{
int refcnt, i;
assert_rv(imgb, OAPV_ERR_INVALID_ARGUMENT);
refcnt = atomic_dec(&imgb->refcnt);
if(refcnt == 0) {
for(i = 0; i < OAPV_MAX_CC; i++) {
if(imgb->baddr[i])
picbuf_free(imgb->baddr[i]);
}
free(imgb);
}
return refcnt;
}
oapv_imgb_t *imgb_create(int w, int h, int cs)
{
int i, bd;
oapv_imgb_t *imgb;
imgb = (oapv_imgb_t *)malloc(sizeof(oapv_imgb_t));
if(imgb == NULL)
goto ERR;
memset(imgb, 0, sizeof(oapv_imgb_t));
bd = OAPV_CS_GET_BYTE_DEPTH(cs); /* byte unit */
imgb->w[0] = w;
imgb->h[0] = h;
switch(OAPV_CS_GET_FORMAT(cs)) {
case OAPV_CF_YCBCR400:
imgb->w[1] = imgb->w[2] = w;
imgb->h[1] = imgb->h[2] = h;
imgb->np = 1;
break;
case OAPV_CF_YCBCR420:
imgb->w[1] = imgb->w[2] = (w + 1) >> 1;
imgb->h[1] = imgb->h[2] = (h + 1) >> 1;
imgb->np = 3;
break;
case OAPV_CF_YCBCR422:
imgb->w[1] = imgb->w[2] = (w + 1) >> 1;
imgb->h[1] = imgb->h[2] = h;
imgb->np = 3;
break;
case OAPV_CF_YCBCR444:
imgb->w[1] = imgb->w[2] = w;
imgb->h[1] = imgb->h[2] = h;
imgb->np = 3;
break;
case OAPV_CF_YCBCR4444:
imgb->w[1] = imgb->w[2] = imgb->w[3] = w;
imgb->h[1] = imgb->h[2] = imgb->h[3] = h;
imgb->np = 4;
break;
case OAPV_CF_PLANAR2:
imgb->w[1] = w;
imgb->h[1] = h;
imgb->np = 2;
break;
default:
logv3("unsupported color format\n");
goto ERR;
}
for(i = 0; i < imgb->np; i++) {
// width and height need to be aligned to macroblock size
imgb->aw[i] = ALIGN_VAL(imgb->w[i], OAPV_MB_W);
imgb->s[i] = imgb->aw[i] * bd;
imgb->ah[i] = ALIGN_VAL(imgb->h[i], OAPV_MB_H);
imgb->e[i] = imgb->ah[i];
imgb->bsize[i] = imgb->s[i] * imgb->e[i];
imgb->a[i] = imgb->baddr[i] = picbuf_alloc(imgb->bsize[i]);
assert_g(imgb->a[i] != NULL, ERR);
memset(imgb->a[i], 0, imgb->bsize[i]);
}
imgb->cs = cs;
imgb->addref = imgb_addref;
imgb->getref = imgb_getref;
imgb->release = imgb_release;
imgb->addref(imgb); /* increase reference count */
return imgb;
ERR:
logerr("cannot create image buffer\n");
if(imgb) {
for(int i = 0; i < OAPV_MAX_CC; i++) {
if(imgb->a[i])
picbuf_free(imgb->a[i]);
}
free(imgb);
}
return NULL;
}
static int imgb_read(FILE *fp, oapv_imgb_t *img, int width, int height, int is_y4m)
{
int f_w, f_h;
unsigned char *p8;
/* handling Y4M frame header */
char t_buf[10];
if(is_y4m) {
if(6 != fread(t_buf, 1, 6, fp))
return -1;
if(memcmp(t_buf, "FRAME", 5)) {
logerr("Loss of framing in Y4M input data\n");
return -1;
}
if(t_buf[5] != '\n') {
logerr("Error parsing Y4M frame header\n");
return -1;
}
}
/* reading YUV format */
int chroma_format = OAPV_CS_GET_FORMAT(img->cs);
int bit_depth = OAPV_CS_GET_BIT_DEPTH(img->cs);
int w_shift = (chroma_format == OAPV_CF_YCBCR420) || ((chroma_format == OAPV_CF_YCBCR422) || (chroma_format == OAPV_CF_PLANAR2)) ? 1 : 0;
int h_shift = chroma_format == OAPV_CF_YCBCR420 ? 1 : 0;
if(bit_depth == 8) {
f_w = width;
f_h = height;
}
else if(bit_depth >= 10 && bit_depth <= 14) {
f_w = width * sizeof(short);
f_h = height;
}
else {
logerr("not supported bit-depth (%d)\n", bit_depth);
return -1;
}
p8 = (unsigned char *)img->a[0];
for(int j = 0; j < f_h; j++) {
if(fread(p8, 1, f_w, fp) != (unsigned)f_w) {
return -1;
}
p8 += img->s[0];
}
if(chroma_format == OAPV_CF_PLANAR2) {
p8 = (unsigned char *)img->a[1];
for(int j = 0; j < f_h; j++) {
if(fread(p8, 1, f_w, fp) != (unsigned)f_w) {
return -1;
}
p8 += img->s[1];
}
}
else if(chroma_format != OAPV_CF_YCBCR400) {
f_w = f_w >> w_shift;
f_h = f_h >> h_shift;
p8 = (unsigned char *)img->a[1];
for(int j = 0; j < f_h; j++) {
if(fread(p8, 1, f_w, fp) != (unsigned)f_w) {
return -1;
}
p8 += img->s[1];
}
p8 = (unsigned char *)img->a[2];
for(int j = 0; j < f_h; j++) {
if(fread(p8, 1, f_w, fp) != (unsigned)f_w) {
return -1;
}
p8 += img->s[2];
}
}
if(chroma_format == OAPV_CF_YCBCR4444) {
f_w = f_w >> w_shift;
f_h = f_h >> h_shift;
p8 = (unsigned char *)img->a[3];
for(int j = 0; j < f_h; j++) {
if(fread(p8, 1, f_w, fp) != (unsigned)f_w) {
return -1;
}
p8 += img->s[3];
}
}
return 0;
}
static int imgb_write(char *fname, oapv_imgb_t *imgb)
{
unsigned char *p8;
int i, j, bd;
FILE *fp;
int chroma_format = OAPV_CS_GET_FORMAT(imgb->cs);
int bit_depth = OAPV_CS_GET_BIT_DEPTH(imgb->cs);
fp = fopen(fname, "ab");
if(fp == NULL) {
logerr("cannot open file = %s\n", fname);
return -1;
}
if(bit_depth == 8 && (chroma_format == OAPV_CF_YCBCR400 || chroma_format == OAPV_CF_YCBCR420 || chroma_format == OAPV_CF_YCBCR422 ||
chroma_format == OAPV_CF_YCBCR444 || chroma_format == OAPV_CF_YCBCR4444)) {
bd = 1;
}
else if(bit_depth >= 10 && bit_depth <= 14 && (chroma_format == OAPV_CF_YCBCR400 || chroma_format == OAPV_CF_YCBCR420 || chroma_format == OAPV_CF_YCBCR422 || chroma_format == OAPV_CF_YCBCR444 || chroma_format == OAPV_CF_YCBCR4444)) {
bd = 2;
}
else if(bit_depth >= 10 && chroma_format == OAPV_CF_PLANAR2) {
bd = 2;
}
else {
logerr("cannot support the color space\n");
fclose(fp);
return -1;
}
for(i = 0; i < imgb->np; i++) {
p8 = (unsigned char *)imgb->a[i] + (imgb->s[i] * imgb->y[i]) + (imgb->x[i] * bd);
for(j = 0; j < imgb->h[i]; j++) {
fwrite(p8, imgb->w[i] * bd, 1, fp);
p8 += imgb->s[i];
}
}
fclose(fp);
return 0;
}
static void imgb_cpy_plane(oapv_imgb_t *dst, oapv_imgb_t *src)
{
int i, j;
unsigned char *s, *d;
int numbyte = OAPV_CS_GET_BYTE_DEPTH(src->cs);
for(i = 0; i < src->np; i++) {
s = (unsigned char *)src->a[i];
d = (unsigned char *)dst->a[i];
for(j = 0; j < src->ah[i]; j++) {
memcpy(d, s, numbyte * src->aw[i]);
s += src->s[i];
d += dst->s[i];
}
}
}
static void imgb_cpy_shift_left_8b(oapv_imgb_t *dst, oapv_imgb_t *src, int shift)
{
int i, j, k;
unsigned char *s;
short *d;
for(i = 0; i < dst->np; i++) {
s = (unsigned char *)src->a[i];
d = (short *)dst->a[i];
for(j = 0; j < src->ah[i]; j++) {
for(k = 0; k < src->aw[i]; k++) {
d[k] = (short)(s[k] << shift);
}
s = s + src->s[i];
d = (short *)(((unsigned char *)d) + dst->s[i]);
}
}
}
static void imgb_cpy_shift_right_8b(oapv_imgb_t *dst, oapv_imgb_t *src, int shift)
{
int i, j, k, t0, add;
short *s;
unsigned char *d;
if(shift)
add = 1 << (shift - 1);
else
add = 0;
for(i = 0; i < dst->np; i++) {
s = (short *)src->a[i];
d = (unsigned char *)dst->a[i];
for(j = 0; j < src->ah[i]; j++) {
for(k = 0; k < src->aw[i]; k++) {
t0 = ((s[k] + add) >> shift);
d[k] = (unsigned char)(CLIP_VAL(t0, 0, 255));
}
s = (short *)(((unsigned char *)s) + src->s[i]);
d = d + dst->s[i];
}
}
}
static void imgb_cpy_shift_left(oapv_imgb_t *dst, oapv_imgb_t *src, int shift)
{
int i, j, k;
unsigned short *s;
unsigned short *d;
for(i = 0; i < dst->np; i++) {
s = (unsigned short *)src->a[i];
d = (unsigned short *)dst->a[i];
for(j = 0; j < src->h[i]; j++) {
for(k = 0; k < src->w[i]; k++) {
d[k] = (unsigned short)(s[k] << shift);
}
s = (unsigned short *)(((unsigned char *)s) + src->s[i]);
d = (unsigned short *)(((unsigned char *)d) + dst->s[i]);
}
}
}
static void imgb_cpy_shift_right(oapv_imgb_t *dst, oapv_imgb_t *src, int shift)
{
int i, j, k, t0, add;
int clip_min = 0;
int clip_max = 0;
unsigned short *s;
unsigned short *d;
if(shift)
add = 1 << (shift - 1);
else
add = 0;
clip_max = (1 << (OAPV_CS_GET_BIT_DEPTH(dst->cs))) - 1;
for(i = 0; i < dst->np; i++) {
s = (unsigned short *)src->a[i];
d = (unsigned short *)dst->a[i];
for(j = 0; j < src->h[i]; j++) {
for(k = 0; k < src->w[i]; k++) {
t0 = ((s[k] + add) >> shift);
d[k] = (CLIP_VAL(t0, clip_min, clip_max));
}
s = (unsigned short *)(((unsigned char *)s) + src->s[i]);
d = (unsigned short *)(((unsigned char *)d) + dst->s[i]);
}
}
}
static void imgb_cpy(oapv_imgb_t *dst, oapv_imgb_t *src)
{
int i, bd_src, bd_dst;
bd_src = OAPV_CS_GET_BIT_DEPTH(src->cs);
bd_dst = OAPV_CS_GET_BIT_DEPTH(dst->cs);
if(src->cs == dst->cs) {
imgb_cpy_plane(dst, src);
}
else if(bd_src == 8 && bd_dst > 8) {
imgb_cpy_shift_left_8b(dst, src, bd_dst - bd_src);
}
else if(bd_src > 8 && bd_dst == 8) {
imgb_cpy_shift_right_8b(dst, src, bd_src - bd_dst);
}
else if(bd_src < bd_dst) {
imgb_cpy_shift_left(dst, src, bd_dst - bd_src);
}
else if(bd_src > bd_dst) {
imgb_cpy_shift_right(dst, src, bd_src - bd_dst);
}
else {
logerr("ERROR: unsupported image copy\n");
return;
}
for(i = 0; i < OAPV_MAX_CC; i++) {
dst->x[i] = src->x[i];
dst->y[i] = src->y[i];
dst->w[i] = src->w[i];
dst->h[i] = src->h[i];
dst->ts[i] = src->ts[i];
}
}
static void measure_psnr(oapv_imgb_t *org, oapv_imgb_t *rec, double psnr[4], int bit_depth)
{
double sum[4], mse[4];
if(bit_depth == 8) {
unsigned char *o, *r;
int i, j, k;
for(i = 0; i < org->np; i++) {
o = (unsigned char *)org->a[i];
r = (unsigned char *)rec->a[i];
sum[i] = 0;
for(j = 0; j < org->h[i]; j++) {
for(k = 0; k < org->w[i]; k++) {
sum[i] += (o[k] - r[k]) * (o[k] - r[k]);
}
o += org->s[i];
r += rec->s[i];
}
mse[i] = sum[i] / (org->w[i] * org->h[i]);
psnr[i] = (mse[i] == 0.0) ? 100. : fabs(10 * log10(((255 * 255) / mse[i])));
}
}
else {
/* more than 8bit, ex) 10bit */
unsigned short *o, *r;
int i, j, k;
int factor = 1 << (bit_depth - 8);
factor *= factor;
for(i = 0; i < org->np; i++) {
o = (unsigned short *)org->a[i];
r = (unsigned short *)rec->a[i];
sum[i] = 0;
for(j = 0; j < org->h[i]; j++) {
for(k = 0; k < org->w[i]; k++) {
if(OAPV_CS_GET_FORMAT(org->cs) == OAPV_CF_PLANAR2) {
sum[i] += (((int)o[k] - (int)r[k]) >> 6) * (((int)o[k] - (int)r[k]) >> 6);
}
else {
sum[i] += (o[k] - r[k]) * (o[k] - r[k]);
}
}
o = (unsigned short *)((unsigned char *)o + org->s[i]);
r = (unsigned short *)((unsigned char *)r + rec->s[i]);
}
mse[i] = sum[i] / (org->w[i] * org->h[i]);
psnr[i] = (mse[i] == 0.0) ? 100. : fabs(10 * log10(((255 * 255 * factor) / mse[i])));
}
}
}
static int write_data(char *fname, unsigned char *data, int size)
{
FILE *fp;
fp = fopen(fname, "ab");
if(fp == NULL) {
logerr("cannot open an writing file=%s\n", fname);
return -1;
}
fwrite(data, 1, size, fp);
fclose(fp);
return 0;
}
static int clear_data(char *fname)
{
FILE *fp;
fp = fopen(fname, "wb");
if(fp == NULL) {
logerr("cannot remove file (%s)\n", fname);
return -1;
}
fclose(fp);
return 0;
}
static unsigned char char_to_hex(char a)
{
unsigned char ret;
switch(a) {
case 'a':
case 'A':
ret = 10;
break;
case 'b':
case 'B':
ret = 11;
break;
case 'c':
case 'C':
ret = 12;
break;
case 'd':
case 'D':
ret = 13;
break;
case 'e':
case 'E':
ret = 14;
break;
case 'f':
case 'F':
ret = 15;
break;
default:
ret = (unsigned char)a - '0';
break;
}
return ret;
}
#endif /* _OAPV_APP_UTIL_H_ */