/* * 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 #include #include #include #include #include #include #include #if LINUX #include #include #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 #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 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 #include 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_ */