diff --git a/.dockerignore b/.dockerignore index 3098be75a..ebbccc08e 100644 --- a/.dockerignore +++ b/.dockerignore @@ -2,3 +2,7 @@ matlab workspace python/.tox .git +.venv +*.mp4 +*.yuv +*.mov diff --git a/.github/workflows/libvmaf.yml b/.github/workflows/libvmaf.yml index ce8454be7..02becf032 100644 --- a/.github/workflows/libvmaf.yml +++ b/.github/workflows/libvmaf.yml @@ -78,7 +78,7 @@ jobs: - name: Run tox tests run: | mkdir -p ~/.ccache && sudo chown -R $(whoami) ~/.ccache - pip install 'tox<4' + pip install tox tox -c python/ -e py -- -v -p no:warnings -m 'main or lib' --doctest-modules - name: Get binary path & Current Release @@ -91,7 +91,7 @@ jobs: - name: Upload vmaf uses: actions/upload-artifact@v4 with: - name: ${{ matrix.os }}-vmaf + name: ${{ matrix.os }}-${{ matrix.CC }}-vmaf path: ${{ steps.get_info.outputs.path }} - name: Upload vmaf if: steps.get_info.outputs.upload_url != 'null' diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 2b047cfb0..ee0bb54ce 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -30,7 +30,7 @@ jobs: with: fetch-depth: 0 - name: Cache ccache files - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | .ccache diff --git a/Dockerfile.cuda b/Dockerfile.cuda index c4bc1f708..47f5a0f71 100644 --- a/Dockerfile.cuda +++ b/Dockerfile.cuda @@ -1,18 +1,20 @@ -ARG CUDA_VERSION=12.1.0 +ARG CUDA_VERSION=12.3.1 +ARG VMAF_TAG=master +ARG FFFMPEG_TAG=master # By copying the installation from a devel to a runtime container one could likely save a lot container size FROM nvidia/cuda:$CUDA_VERSION-devel-ubuntu22.04 RUN DEBIAN_FRONTEND=noninteractive apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y libopenjp2-7-dev \ ninja-build cmake git python3 python3-pip nasm xxd pkg-config curl unzip -RUN git clone https://github.com/Netflix/vmaf.git +RUN git clone https://github.com/Netflix/vmaf.git && cd vmaf && git checkout $VMAF_TAG -RUN git clone https://github.com/FFmpeg/FFmpeg.git +RUN git clone https://github.com/FFmpeg/FFmpeg.git && cd FFmpeg && git checkout $FFFMPEG_TAG RUN git clone https://github.com/FFmpeg/nv-codec-headers.git && cd nv-codec-headers && make && make install # install vmaf -RUN python3 -m pip install meson cpython +RUN python3 -m pip install meson RUN cd vmaf && meson libvmaf/build libvmaf -Denable_cuda=true -Denable_avx512=true --buildtype release && \ ninja -vC libvmaf/build && \ ninja -vC libvmaf/build install diff --git a/libvmaf/include/libvmaf/libvmaf.h b/libvmaf/include/libvmaf/libvmaf.h index 31c5af727..71d39e656 100644 --- a/libvmaf/include/libvmaf/libvmaf.h +++ b/libvmaf/include/libvmaf/libvmaf.h @@ -55,6 +55,31 @@ enum VmafPoolingMethod { VMAF_POOL_METHOD_NB }; +/** + * @struct VmafConfiguration + * @brief Configuration needed to initialize a `VmafContext` + * + * @param log_level How verbose the logger is. + * + * @param n_threads How many threads can be used to run + * feature extractors concurrently. + * + * @param n_subsample Compute scores only every N frames. + * Note that setting an even value for N can lead to + * inaccurate results. For more detail, see + * https://github.com/Netflix/vmaf/issues/1214 + * + * @param cpumask Restrict permitted CPU instruction sets. + * if cpumask & 1: disable SSE2 / disable NEON (on arm64) + * if cpumask & 2: disable SSE3/SSSE3 + * if cpumask & 4: disable SSE4.1 + * if cpumask & 8: disable AVX2 + * if cpumask & 16: disable AVX512 + * if cpumask & 32: disable AVX512ICL + * + * @param gpumask Restrict permitted GPU operations. + * if gpumask: disable CUDA + */ typedef struct VmafConfiguration { enum VmafLogLevel log_level; unsigned n_threads; diff --git a/libvmaf/src/feature/cambi.c b/libvmaf/src/feature/cambi.c index 4b4b2d6fa..094e24c32 100644 --- a/libvmaf/src/feature/cambi.c +++ b/libvmaf/src/feature/cambi.c @@ -83,11 +83,13 @@ typedef struct CambiBuffers { uint16_t *filter_mode_buffer; uint16_t *diffs_to_consider; uint16_t *tvi_for_diff; + uint16_t *derivative_buffer; int *diff_weights; int *all_diffs; } CambiBuffers; typedef void (*VmafRangeUpdater)(uint16_t *arr, int left, int right); +typedef void (*VmafDerivativeCalculator)(const uint16_t *image_data, uint16_t *derivative_buffer, int width, int height, int row, int stride); typedef struct CambiState { VmafPicture pics[PICS_BUFFER_SIZE]; @@ -107,6 +109,7 @@ typedef struct CambiState { FILE *heatmaps_files[NUM_SCALES]; VmafRangeUpdater inc_range_callback; VmafRangeUpdater dec_range_callback; + VmafDerivativeCalculator derivative_callback; CambiBuffers buffers; } CambiState; @@ -322,6 +325,14 @@ static void decrement_range(uint16_t *arr, int left, int right) { } } +static void get_derivative_data_for_row(const uint16_t *image_data, uint16_t *derivative_buffer, int width, int height, int row, int stride) { + for (int col = 0; col < width; col++) { + bool horizontal_derivative = (col == width - 1 || image_data[row * stride + col] == image_data[row * stride + col + 1]); + bool vertical_derivative = (row == height - 1 || image_data[row * stride + col] == image_data[(row + 1) * stride + col]); + derivative_buffer[col] = horizontal_derivative && vertical_derivative; + } +} + #ifdef _WIN32 #define PATH_SEPARATOR '\\' #else @@ -405,6 +416,8 @@ static int init(VmafFeatureExtractor *fex, enum VmafPixelFormat pix_fmt, if (!s->buffers.mask_dp) return -ENOMEM; s->buffers.filter_mode_buffer = aligned_malloc(ALIGN_CEIL(3 * alloc_w * sizeof(uint16_t)), 32); if (!s->buffers.filter_mode_buffer) return -ENOMEM; + s->buffers.derivative_buffer = aligned_malloc(ALIGN_CEIL(alloc_w * sizeof(uint16_t)), 32); + if (!s->buffers.derivative_buffer) return -ENOMEM; if (s->heatmaps_path) { int err = mkdirp(s->heatmaps_path, 0770); @@ -428,12 +441,14 @@ static int init(VmafFeatureExtractor *fex, enum VmafPixelFormat pix_fmt, s->inc_range_callback = increment_range; s->dec_range_callback = decrement_range; + s->derivative_callback = get_derivative_data_for_row; #if ARCH_X86 unsigned flags = vmaf_get_cpu_flags(); if (flags & VMAF_X86_CPU_FLAG_AVX2) { s->inc_range_callback = cambi_increment_range_avx2; s->dec_range_callback = cambi_decrement_range_avx2; + s->derivative_callback = get_derivative_data_for_row_avx2; } #endif @@ -638,8 +653,8 @@ static inline uint16_t mode3(uint16_t a, uint16_t b, uint16_t c) { static void filter_mode(const VmafPicture *image, int width, int height, uint16_t *buffer) { uint16_t *data = image->data[0]; ptrdiff_t stride = image->stride[0] >> 1; + int curr_line = 0; for (int i = 0; i < height; i++) { - int curr_line = i % 3; buffer[curr_line * width + 0] = data[i * stride + 0]; for (int j = 1; j < width - 1; j++) { buffer[curr_line * width + j] = mode3(data[i * stride + j - 1], data[i * stride + j], data[i * stride + j + 1]); @@ -651,6 +666,7 @@ static void filter_mode(const VmafPicture *image, int width, int height, uint16_ data[(i - 1) * stride + j] = mode3(buffer[0 * width + j], buffer[1 * width + j], buffer[2 * width + j]); } } + curr_line = (curr_line + 1 == 3 ? 0 : curr_line + 1); } } @@ -673,11 +689,6 @@ static FORCE_INLINE inline uint16_t get_mask_index(unsigned input_width, unsigne return (filter_size * filter_size + 3 * (ceil_log2(shifted_wh) - 11) - 1)>>1; } -static FORCE_INLINE inline bool get_derivative_data(const uint16_t *data, int width, int height, int i, int j, ptrdiff_t stride) { - return (i == height - 1 || (data[i * stride + j] == data[(i + 1) * stride + j])) && - (j == width - 1 || (data[i * stride + j] == data[i * stride + j + 1])); -} - /* * This function calculates the horizontal and vertical derivatives of the image using 2x1 and 1x2 kernels. * We say a pixel has zero_derivative=1 if it's equal to its right and bottom neighbours, and =0 otherwise (edges also count as "equal"). @@ -687,8 +698,8 @@ static FORCE_INLINE inline bool get_derivative_data(const uint16_t *data, int wi * To save memory, it uses a DP matrix of only the necessary size, rather than the full matrix, and indexes its rows cyclically. */ static void get_spatial_mask_for_index(const VmafPicture *image, VmafPicture *mask, - uint32_t *dp, uint16_t mask_index, uint16_t filter_size, - int width, int height) { + uint32_t *dp, uint16_t *derivative_buffer, uint16_t mask_index, + uint16_t filter_size, int width, int height, VmafDerivativeCalculator derivative_callback) { uint16_t pad_size = filter_size >> 1; uint16_t *image_data = image->data[0]; uint16_t *mask_data = mask->data[0]; @@ -700,8 +711,11 @@ static void get_spatial_mask_for_index(const VmafPicture *image, VmafPicture *ma // Initial computation: fill dp except for the last row for (int i = 0; i < pad_size; i++) { + if (i < height) { + derivative_callback(image_data, derivative_buffer, width, height, i, stride); + } for (int j = 0; j < width + pad_size; j++) { - int value = (i < height && j < width ? get_derivative_data(image_data, width, height, i, j, stride) : 0); + int value = (i < height && j < width ? derivative_buffer[j] : 0); int curr_row = i + pad_size + 1; int curr_col = j + pad_size + 1; dp[curr_row * dp_width + curr_col] = @@ -713,27 +727,31 @@ static void get_spatial_mask_for_index(const VmafPicture *image, VmafPicture *ma } // Start from the last row in the dp matrix + int prev_row = dp_height - 2; int curr_row = dp_height - 1; int curr_compute = pad_size + 1; + int bottom = (curr_compute + pad_size) % dp_height; + int top = (curr_compute + dp_height - pad_size - 1) % dp_height; for (int i = pad_size; i < height + pad_size; i++) { + if (i < height) { + derivative_callback(image_data, derivative_buffer, width, height, i, stride); + } // First compute the values of dp for curr_row for (int j = 0; j < width + pad_size; j++) { - int value = (i < height && j < width ? get_derivative_data(image_data, width, height, i, j, stride) : 0); + int value = (i < height && j < width ? derivative_buffer[j] : 0); int curr_col = j + pad_size + 1; - int prev_row = (curr_row + dp_height - 1) % dp_height; dp[curr_row * dp_width + curr_col] = value + dp[prev_row * dp_width + curr_col] + dp[curr_row * dp_width + curr_col - 1] - dp[prev_row * dp_width + curr_col - 1]; } - curr_row = (curr_row + 1) % dp_height; + prev_row = curr_row; + curr_row = (curr_row + 1 == dp_height ? 0 : curr_row + 1); // Then use the values to compute the square sum for the curr_compute row. for (int j = 0; j < width; j++) { int curr_col = j + pad_size + 1; - int bottom = (curr_compute + pad_size) % dp_height; - int top = (curr_compute + dp_height - pad_size - 1) % dp_height; int right = curr_col + pad_size; int left = curr_col - pad_size - 1; int result = @@ -743,14 +761,17 @@ static void get_spatial_mask_for_index(const VmafPicture *image, VmafPicture *ma + dp[top * dp_width + left]; mask_data[(i - pad_size) * stride + j] = (result > mask_index); } - curr_compute = (curr_compute + 1) % dp_height; + curr_compute = (curr_compute + 1 == dp_height ? 0 : curr_compute + 1); + bottom = (bottom + 1 == dp_height ? 0 : bottom + 1); + top = (top + 1 == dp_height ? 0 : top + 1); } } static void get_spatial_mask(const VmafPicture *image, VmafPicture *mask, - uint32_t *dp, unsigned width, unsigned height) { + uint32_t *dp, uint16_t *derivative_buffer, unsigned width, unsigned height, + VmafDerivativeCalculator derivative_callback) { uint16_t mask_index = get_mask_index(width, height, MASK_FILTER_SIZE); - get_spatial_mask_for_index(image, mask, dp, mask_index, MASK_FILTER_SIZE, width, height); + get_spatial_mask_for_index(image, mask, dp, derivative_buffer, mask_index, MASK_FILTER_SIZE, width, height, derivative_callback); } static float c_value_pixel(const uint16_t *histograms, uint16_t value, const int *diff_weights, @@ -1017,7 +1038,7 @@ static int dump_c_values(FILE *heatmaps_files[], const float *c_values, int widt static int cambi_score(VmafPicture *pics, uint16_t window_size, double topk, const uint16_t num_diffs, const uint16_t *tvi_for_diff, CambiBuffers buffers, VmafRangeUpdater inc_range_callback, VmafRangeUpdater dec_range_callback, - double *score, bool write_heatmaps, FILE *heatmaps_files[], + VmafDerivativeCalculator derivative_callback, double *score, bool write_heatmaps, FILE *heatmaps_files[], int width, int height, int frame) { double scores_per_scale[NUM_SCALES]; VmafPicture *image = &pics[0]; @@ -1026,7 +1047,7 @@ static int cambi_score(VmafPicture *pics, uint16_t window_size, double topk, int scaled_width = width; int scaled_height = height; - get_spatial_mask(image, mask, buffers.mask_dp, width, height); + get_spatial_mask(image, mask, buffers.mask_dp, buffers.derivative_buffer, width, height, derivative_callback); for (unsigned scale = 0; scale < NUM_SCALES; scale++) { if (scale > 0) { scaled_width = (scaled_width + 1) >> 1; @@ -1067,7 +1088,7 @@ static int preprocess_and_extract_cambi(CambiState *s, VmafPicture *pic, double bool write_heatmaps = s->heatmaps_path && !is_src; err = cambi_score(s->pics, window_size, s->topk, num_diffs, s->buffers.tvi_for_diff, - s->buffers, s->inc_range_callback, s->dec_range_callback, score, write_heatmaps, s->heatmaps_files, width, height, frame); + s->buffers, s->inc_range_callback, s->dec_range_callback, s->derivative_callback, score, write_heatmaps, s->heatmaps_files, width, height, frame); if (err) return err; return 0; @@ -1123,6 +1144,7 @@ static int close_cambi(VmafFeatureExtractor *fex) { aligned_free(s->buffers.diffs_to_consider); aligned_free(s->buffers.diff_weights); aligned_free(s->buffers.all_diffs); + aligned_free(s->buffers.derivative_buffer); if (s->heatmaps_path) { for (int scale = 0; scale < NUM_SCALES; scale++) { diff --git a/libvmaf/src/feature/feature_extractor.c b/libvmaf/src/feature/feature_extractor.c index 94a4e16ca..21f9e64e5 100644 --- a/libvmaf/src/feature/feature_extractor.c +++ b/libvmaf/src/feature/feature_extractor.c @@ -386,6 +386,8 @@ int vmaf_fex_ctx_pool_aquire(VmafFeatureExtractorContextPool *pool, } err = vmaf_feature_extractor_context_create(&f, entry->fex, d); if (err) goto unlock; + if (f->fex->flags & VMAF_FEATURE_FRAME_SYNC) + f->fex->framesync = (fex->framesync); } if (!entry->ctx_list[i].in_use) { entry->ctx_list[i].fex_ctx = *fex_ctx = f; diff --git a/libvmaf/src/feature/feature_extractor.h b/libvmaf/src/feature/feature_extractor.h index e34188c3b..574436e76 100644 --- a/libvmaf/src/feature/feature_extractor.h +++ b/libvmaf/src/feature/feature_extractor.h @@ -24,6 +24,7 @@ #include #include "dict.h" +#include "framesync.h" #include "feature_collector.h" #include "opt.h" @@ -36,6 +37,7 @@ enum VmafFeatureExtractorFlags { VMAF_FEATURE_EXTRACTOR_TEMPORAL = 1 << 0, VMAF_FEATURE_EXTRACTOR_CUDA = 1 << 1, + VMAF_FEATURE_FRAME_SYNC = 1 << 2, }; typedef struct VmafFeatureExtractor { @@ -94,6 +96,8 @@ typedef struct VmafFeatureExtractor { VmafCudaState *cu_state; ///< VmafCudaState, set by framework #endif + VmafFrameSyncContext *framesync; + } VmafFeatureExtractor; VmafFeatureExtractor *vmaf_get_feature_extractor_by_name(const char *name); diff --git a/libvmaf/src/feature/float_adm.c b/libvmaf/src/feature/float_adm.c index 488298514..60d1986ec 100644 --- a/libvmaf/src/feature/float_adm.c +++ b/libvmaf/src/feature/float_adm.c @@ -22,6 +22,7 @@ #include "dict.h" #include "feature_collector.h" +#include "framesync.h" #include "feature_extractor.h" #include "feature_name.h" diff --git a/libvmaf/src/feature/picture_copy.c b/libvmaf/src/feature/picture_copy.c index 989df124f..cedf466ed 100644 --- a/libvmaf/src/feature/picture_copy.c +++ b/libvmaf/src/feature/picture_copy.c @@ -39,12 +39,16 @@ void picture_copy_hbd(float *dst, ptrdiff_t dst_stride, void picture_copy(float *dst, ptrdiff_t dst_stride, VmafPicture *src, int offset, unsigned bpc) { - if (bpc == 10) - return picture_copy_hbd(dst, dst_stride, src, offset, 4.0f); - else if (bpc == 12) - return picture_copy_hbd(dst, dst_stride, src, offset, 16.0f); - else if (bpc == 16) - return picture_copy_hbd(dst, dst_stride, src, offset, 256.0f); + if (bpc == 10) { + picture_copy_hbd(dst, dst_stride, src, offset, 4.0f); + return; + } else if (bpc == 12) { + picture_copy_hbd(dst, dst_stride, src, offset, 16.0f); + return; + } else if (bpc == 16) { + picture_copy_hbd(dst, dst_stride, src, offset, 256.0f); + return; + } float *float_data = dst; uint8_t *data = src->data[0]; diff --git a/libvmaf/src/feature/x86/cambi_avx2.c b/libvmaf/src/feature/x86/cambi_avx2.c index 41cffbf6e..ba3deec6e 100644 --- a/libvmaf/src/feature/x86/cambi_avx2.c +++ b/libvmaf/src/feature/x86/cambi_avx2.c @@ -18,6 +18,7 @@ #include #include +#include void cambi_increment_range_avx2(uint16_t *arr, int left, int right) { __m256i val_vector = _mm256_set1_epi16(1); @@ -44,3 +45,39 @@ void cambi_decrement_range_avx2(uint16_t *arr, int left, int right) { arr[col]--; } } + +void get_derivative_data_for_row_avx2(const uint16_t *image_data, uint16_t *derivative_buffer, int width, int height, int row, int stride) { + // For the last row, we only compute horizontal derivatives + if (row == height - 1) { + __m256i ones = _mm256_set1_epi16(1); + int col = 0; + for (; col + 15 < width - 1; col += 16) { + __m256i vals1 = _mm256_loadu_si256((__m256i*) &image_data[row * stride + col]); + __m256i vals2 = _mm256_loadu_si256((__m256i*) &image_data[row * stride + col + 1]); + __m256i result = _mm256_cmpeq_epi16(vals1, vals2); + _mm256_storeu_si256((__m256i*) &derivative_buffer[col], _mm256_and_si256(ones, result)); + } + for (; col < width - 1; col++) { + derivative_buffer[col] = (image_data[row * stride + col] == image_data[row * stride + col + 1]); + } + derivative_buffer[width - 1] = 1; + } + else { + __m256i ones = _mm256_set1_epi16(1); + int col = 0; + for (; col + 15 < width - 1; col += 16) { + __m256i horiz_vals1 = _mm256_loadu_si256((__m256i*) &image_data[row * stride + col]); + __m256i horiz_vals2 = _mm256_loadu_si256((__m256i*) &image_data[row * stride + col + 1]); + __m256i horiz_result = _mm256_and_si256(ones, _mm256_cmpeq_epi16(horiz_vals1, horiz_vals2)); + __m256i vert_vals1 = _mm256_loadu_si256((__m256i*) &image_data[row * stride + col]); + __m256i vert_vals2 = _mm256_loadu_si256((__m256i*) &image_data[(row + 1) * stride + col]); + __m256i vert_result = _mm256_and_si256(ones, _mm256_cmpeq_epi16(vert_vals1, vert_vals2)); + _mm256_storeu_si256((__m256i*) &derivative_buffer[col], _mm256_and_si256(horiz_result, vert_result)); + } + for (; col < width; col++) { + bool horizontal_derivative = (col == width - 1 || image_data[row * stride + col] == image_data[row * stride + col + 1]); + bool vertical_derivative = image_data[row * stride + col] == image_data[(row + 1) * stride + col]; + derivative_buffer[col] = horizontal_derivative && vertical_derivative; + } + } +} \ No newline at end of file diff --git a/libvmaf/src/feature/x86/cambi_avx2.h b/libvmaf/src/feature/x86/cambi_avx2.h index e1bdbbdc4..9388ca577 100644 --- a/libvmaf/src/feature/x86/cambi_avx2.h +++ b/libvmaf/src/feature/x86/cambi_avx2.h @@ -26,4 +26,6 @@ void cambi_increment_range_avx2(uint16_t *arr, int left, int right); void cambi_decrement_range_avx2(uint16_t *arr, int left, int right); +void get_derivative_data_for_row_avx2(const uint16_t *image_data, uint16_t *derivative_buffer, int width, int height, int row, int stride); + #endif /* X86_AVX2_CAMBI_H_ */ diff --git a/libvmaf/src/framesync.c b/libvmaf/src/framesync.c new file mode 100644 index 000000000..5c4c57758 --- /dev/null +++ b/libvmaf/src/framesync.c @@ -0,0 +1,227 @@ +/** + * + * Copyright 2016-2023 Netflix, Inc. + * + * Licensed under the BSD+Patent License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSDplusPatent + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include +#include +#include "framesync.h" + +enum { + BUF_FREE = 0, + BUF_ACQUIRED, + BUF_FILLED, + BUF_RETRIEVED, +}; + +typedef struct VmafFrameSyncBuf { + void *frame_data; + int buf_status; + signed long index; + struct VmafFrameSyncBuf *next; +} VmafFrameSyncBuf; + +typedef struct VmafFrameSyncContext { + VmafFrameSyncBuf *buf_que; + pthread_mutex_t acquire_lock; + pthread_mutex_t retrieve_lock; + pthread_cond_t retrieve; + unsigned buf_cnt; +} VmafFrameSyncContext; + +int vmaf_framesync_init(VmafFrameSyncContext **fs_ctx) +{ + VmafFrameSyncContext *const ctx = *fs_ctx = malloc(sizeof(VmafFrameSyncContext)); + if (!ctx) return -ENOMEM; + memset(ctx, 0, sizeof(VmafFrameSyncContext)); + ctx->buf_cnt = 1; + + pthread_mutex_init(&(ctx->acquire_lock), NULL); + pthread_mutex_init(&(ctx->retrieve_lock), NULL); + pthread_cond_init(&(ctx->retrieve), NULL); + + VmafFrameSyncBuf *buf_que = ctx->buf_que = malloc(sizeof(VmafFrameSyncBuf)); + + buf_que->frame_data = NULL; + buf_que->buf_status = BUF_FREE; + buf_que->index = -1; + buf_que->next = NULL; + + return 0; +} + +int vmaf_framesync_acquire_new_buf(VmafFrameSyncContext *fs_ctx, void **data, + unsigned data_sz, unsigned index) +{ + VmafFrameSyncBuf *buf_que = fs_ctx->buf_que; + *data = NULL; + + pthread_mutex_lock(&(fs_ctx->acquire_lock)); + + // traverse until a free buffer is found + for (unsigned i = 0; i < fs_ctx->buf_cnt; i++) { + if (buf_que->buf_status == BUF_FREE) { + buf_que->frame_data = *data = malloc(data_sz); + if (!buf_que->frame_data) + return -ENOMEM; + buf_que->buf_status = BUF_ACQUIRED; + buf_que->index = index; + break; + } + // move to next node + if (buf_que->next != NULL) + buf_que = buf_que->next; + } + + // create a new node if all nodes are occupied in the list and append to the tail + if (*data == NULL) { + VmafFrameSyncBuf *new_buf_node = malloc(sizeof(VmafFrameSyncBuf)); + buf_que->next = new_buf_node; + new_buf_node->buf_status = BUF_FREE; + new_buf_node->index = -1; + new_buf_node->next = NULL; + fs_ctx->buf_cnt++; + + new_buf_node->frame_data = *data = malloc(data_sz); + if (!new_buf_node->frame_data) + return -ENOMEM; + new_buf_node->buf_status = BUF_ACQUIRED; + new_buf_node->index = index; + } + + pthread_mutex_unlock(&(fs_ctx->acquire_lock)); + + return 0; +} + +int vmaf_framesync_submit_filled_data(VmafFrameSyncContext *fs_ctx, void *data, + unsigned index) +{ + VmafFrameSyncBuf *buf_que = fs_ctx->buf_que; + + pthread_mutex_lock(&(fs_ctx->retrieve_lock)); + + // loop until a matchng buffer is found + for (unsigned i = 0; i < fs_ctx->buf_cnt; i++) { + if ((buf_que->index == index) && (buf_que->buf_status == BUF_ACQUIRED)) { + buf_que->buf_status = BUF_FILLED; + if (data != buf_que->frame_data) + return -1; + break; + } + + // move to next node + if (NULL != buf_que->next) + buf_que = buf_que->next; + } + + pthread_cond_broadcast(&(fs_ctx->retrieve)); + pthread_mutex_unlock(&(fs_ctx->retrieve_lock)); + + return 0; +} + +int vmaf_framesync_retrieve_filled_data(VmafFrameSyncContext *fs_ctx, + void **data, unsigned index) +{ + *data = NULL; + + while (*data == NULL) { + VmafFrameSyncBuf *buf_que = fs_ctx->buf_que; + pthread_mutex_lock(&(fs_ctx->retrieve_lock)); + // loop until a free buffer is found + for (unsigned i = 0; i < fs_ctx->buf_cnt; i++) { + if ((buf_que->index == index) && (buf_que->buf_status == BUF_FILLED)) { + buf_que->buf_status = BUF_RETRIEVED; + *data = buf_que->frame_data; + break; + } + + // move to next node + if (NULL != buf_que->next) + buf_que = buf_que->next; + } + + if (*data == NULL) + pthread_cond_wait(&(fs_ctx->retrieve), &(fs_ctx->retrieve_lock)); + + pthread_mutex_unlock(&(fs_ctx->retrieve_lock)); + } + + return 0; +} + +int vmaf_framesync_release_buf(VmafFrameSyncContext *fs_ctx, void *data, + unsigned index) +{ + VmafFrameSyncBuf *buf_que = fs_ctx->buf_que; + + pthread_mutex_lock(&(fs_ctx->acquire_lock)); + // loop until a matching buffer is found + for (unsigned i = 0; i < fs_ctx->buf_cnt; i++) { + if ((buf_que->index == index) && (buf_que->buf_status == BUF_RETRIEVED)) { + if (data != buf_que->frame_data) + return -1; + + free(buf_que->frame_data); + buf_que->frame_data = NULL; + buf_que->buf_status = BUF_FREE; + buf_que->index = -1; + break; + } + + // move to next node + if (NULL != buf_que->next) + buf_que = buf_que->next; + } + + pthread_mutex_unlock(&(fs_ctx->acquire_lock)); + return 0; +} + +int vmaf_framesync_destroy(VmafFrameSyncContext *fs_ctx) +{ + VmafFrameSyncBuf *buf_que = fs_ctx->buf_que; + VmafFrameSyncBuf *buf_que_tmp; + + pthread_mutex_destroy(&(fs_ctx->acquire_lock)); + pthread_mutex_destroy(&(fs_ctx->retrieve_lock)); + pthread_cond_destroy(&(fs_ctx->retrieve)); + + //check for any data buffers which are not freed + for (unsigned i = 0; i < fs_ctx->buf_cnt; i++) { + if (NULL != buf_que->frame_data) { + free(buf_que->frame_data); + buf_que->frame_data = NULL; + } + + // move to next node + if (NULL != buf_que->next) { + buf_que_tmp = buf_que; + buf_que = buf_que->next; + free(buf_que_tmp); + } else { + free(buf_que); + } + } + + free(fs_ctx); + + return 0; +} diff --git a/libvmaf/src/framesync.h b/libvmaf/src/framesync.h new file mode 100644 index 000000000..08e9a543a --- /dev/null +++ b/libvmaf/src/framesync.h @@ -0,0 +1,46 @@ +/** + * + * Copyright 2016-2023 Netflix, Inc. + * + * Licensed under the BSD+Patent License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSDplusPatent + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef __VMAF_FRAME_SYNC_H__ +#define __VMAF_FRAME_SYNC_H__ + +#include +#include +#include +#include +#include "libvmaf/libvmaf.h" + +typedef struct VmafFrameSyncContext VmafFrameSyncContext; + +int vmaf_framesync_init(VmafFrameSyncContext **fs_ctx); + +int vmaf_framesync_acquire_new_buf(VmafFrameSyncContext *fs_ctx, void **data, + unsigned data_sz, unsigned index); + +int vmaf_framesync_submit_filled_data(VmafFrameSyncContext *fs_ctx, void *data, + unsigned index); + +int vmaf_framesync_retrieve_filled_data(VmafFrameSyncContext *fs_ctx, void **data, + unsigned index); + +int vmaf_framesync_release_buf(VmafFrameSyncContext *fs_ctx, void *data, + unsigned index); + +int vmaf_framesync_destroy(VmafFrameSyncContext *fs_ctx); + +#endif /* __VMAF_FRAME_SYNC_H__ */ diff --git a/libvmaf/src/libvmaf.c b/libvmaf/src/libvmaf.c index 3fbb050c5..fa3fe6787 100644 --- a/libvmaf/src/libvmaf.c +++ b/libvmaf/src/libvmaf.c @@ -56,6 +56,7 @@ typedef struct VmafContext { RegisteredFeatureExtractors registered_feature_extractors; VmafFeatureExtractorContextPool *fex_ctx_pool; VmafThreadPool *thread_pool; + VmafFrameSyncContext *framesync; #ifdef HAVE_CUDA struct { struct { @@ -99,8 +100,10 @@ int vmaf_init(VmafContext **vmaf, VmafConfiguration cfg) vmaf_set_log_level(cfg.log_level); - err = vmaf_feature_collector_init(&(v->feature_collector)); + err = vmaf_framesync_init(&(v->framesync)); if (err) goto free_v; + err = vmaf_feature_collector_init(&(v->feature_collector)); + if (err) goto free_framesync; err = feature_extractor_vector_init(&(v->registered_feature_extractors)); if (err) goto free_feature_collector; @@ -119,6 +122,8 @@ int vmaf_init(VmafContext **vmaf, VmafConfiguration cfg) feature_extractor_vector_destroy(&(v->registered_feature_extractors)); free_feature_collector: vmaf_feature_collector_destroy(v->feature_collector); +free_framesync: + vmaf_framesync_destroy(v->framesync); free_v: free(v); fail: @@ -235,11 +240,20 @@ static int set_fex_cuda_state(VmafFeatureExtractorContext *fex_ctx, #endif +static int set_fex_framesync(VmafFeatureExtractorContext *fex_ctx, + VmafContext *vmaf) +{ + if (fex_ctx->fex->flags & VMAF_FEATURE_FRAME_SYNC) + fex_ctx->fex->framesync = (vmaf->framesync); + return 0; +} + int vmaf_close(VmafContext *vmaf) { if (!vmaf) return -EINVAL; vmaf_thread_pool_wait(vmaf->thread_pool); + vmaf_framesync_destroy(vmaf->framesync); feature_extractor_vector_destroy(&(vmaf->registered_feature_extractors)); vmaf_feature_collector_destroy(vmaf->feature_collector); vmaf_thread_pool_destroy(vmaf->thread_pool); @@ -292,6 +306,7 @@ int vmaf_use_feature(VmafContext *vmaf, const char *feature_name, #ifdef HAVE_CUDA err |= set_fex_cuda_state(fex_ctx, vmaf); #endif + err |= set_fex_framesync(fex_ctx, vmaf); if (err) return err; RegisteredFeatureExtractors *rfe = &(vmaf->registered_feature_extractors); @@ -339,6 +354,7 @@ int vmaf_use_features_from_model(VmafContext *vmaf, VmafModel *model) #ifdef HAVE_CUDA err |= set_fex_cuda_state(fex_ctx, vmaf); #endif + err |= set_fex_framesync(fex_ctx, vmaf); if (err) return err; err = feature_extractor_vector_append(rfe, fex_ctx, 0); if (err) { @@ -405,6 +421,7 @@ static int threaded_read_pictures(VmafContext *vmaf, VmafPicture *ref, continue; } + fex->framesync = vmaf->framesync; VmafFeatureExtractorContext *fex_ctx; err = vmaf_fex_ctx_pool_aquire(vmaf->fex_ctx_pool, fex, opts_dict, &fex_ctx); diff --git a/libvmaf/src/meson.build b/libvmaf/src/meson.build index 623639d4e..065c59e20 100644 --- a/libvmaf/src/meson.build +++ b/libvmaf/src/meson.build @@ -461,6 +461,7 @@ libvmaf_sources = [ src_dir + 'read_json_model.c', src_dir + 'pdjson.c', src_dir + 'log.c', + src_dir + 'framesync.c', ] if is_cuda_enabled diff --git a/libvmaf/src/model.c b/libvmaf/src/model.c index 4661ec148..ab623a9fd 100644 --- a/libvmaf/src/model.c +++ b/libvmaf/src/model.c @@ -21,24 +21,24 @@ typedef struct VmafBuiltInModel { #if VMAF_BUILT_IN_MODELS #if VMAF_FLOAT_FEATURES -extern const char src_vmaf_float_v0_6_1neg_json; +extern const char src_vmaf_float_v0_6_1neg_json[]; extern const int src_vmaf_float_v0_6_1neg_json_len; -extern const char src_vmaf_float_v0_6_1_json; +extern const char src_vmaf_float_v0_6_1_json[]; extern const int src_vmaf_float_v0_6_1_json_len; -extern const char src_vmaf_float_b_v0_6_3_json; +extern const char src_vmaf_float_b_v0_6_3_json[]; extern const int src_vmaf_float_b_v0_6_3_json_len; -extern const char src_vmaf_float_4k_v0_6_1_json; +extern const char src_vmaf_float_4k_v0_6_1_json[]; extern const int src_vmaf_float_4k_v0_6_1_json_len; #endif -extern const char src_vmaf_v0_6_1_json; +extern const char src_vmaf_v0_6_1_json[]; extern const int src_vmaf_v0_6_1_json_len; -extern const char src_vmaf_b_v0_6_3_json; +extern const char src_vmaf_b_v0_6_3_json[]; extern const int src_vmaf_b_v0_6_3_json_len; -extern const char src_vmaf_v0_6_1neg_json; +extern const char src_vmaf_v0_6_1neg_json[]; extern const int src_vmaf_v0_6_1neg_json_len; -extern const char src_vmaf_4k_v0_6_1_json; +extern const char src_vmaf_4k_v0_6_1_json[]; extern const int src_vmaf_4k_v0_6_1_json_len; -extern const char src_vmaf_4k_v0_6_1neg_json; +extern const char src_vmaf_4k_v0_6_1neg_json[]; extern const int src_vmaf_4k_v0_6_1neg_json_len; #endif @@ -47,48 +47,48 @@ static const VmafBuiltInModel built_in_models[] = { #if VMAF_FLOAT_FEATURES { .version = "vmaf_float_v0.6.1", - .data = &src_vmaf_float_v0_6_1_json, + .data = src_vmaf_float_v0_6_1_json, .data_len = &src_vmaf_float_v0_6_1_json_len, }, { .version = "vmaf_float_b_v0.6.3", - .data = &src_vmaf_float_b_v0_6_3_json, + .data = src_vmaf_float_b_v0_6_3_json, .data_len = &src_vmaf_float_b_v0_6_3_json_len, }, { .version = "vmaf_float_v0.6.1neg", - .data = &src_vmaf_float_v0_6_1neg_json, + .data = src_vmaf_float_v0_6_1neg_json, .data_len = &src_vmaf_float_v0_6_1neg_json_len, }, { .version = "vmaf_float_4k_v0.6.1", - .data = &src_vmaf_float_4k_v0_6_1_json, + .data = src_vmaf_float_4k_v0_6_1_json, .data_len = &src_vmaf_float_4k_v0_6_1_json_len, }, #endif { .version = "vmaf_v0.6.1", - .data = &src_vmaf_v0_6_1_json, + .data = src_vmaf_v0_6_1_json, .data_len = &src_vmaf_v0_6_1_json_len, }, { .version = "vmaf_b_v0.6.3", - .data = &src_vmaf_b_v0_6_3_json, + .data = src_vmaf_b_v0_6_3_json, .data_len = &src_vmaf_b_v0_6_3_json_len, }, { .version = "vmaf_v0.6.1neg", - .data = &src_vmaf_v0_6_1neg_json, + .data = src_vmaf_v0_6_1neg_json, .data_len = &src_vmaf_v0_6_1neg_json_len, }, { .version = "vmaf_4k_v0.6.1", - .data = &src_vmaf_4k_v0_6_1_json, + .data = src_vmaf_4k_v0_6_1_json, .data_len = &src_vmaf_4k_v0_6_1_json_len, }, { .version = "vmaf_4k_v0.6.1neg", - .data = &src_vmaf_4k_v0_6_1neg_json, + .data = src_vmaf_4k_v0_6_1neg_json, .data_len = &src_vmaf_4k_v0_6_1neg_json_len, }, #endif @@ -131,9 +131,9 @@ char *vmaf_model_generate_name(VmafModelConfig *cfg) memset(name, 0, name_sz); if (!cfg->name) - strncpy(name, default_name, name_sz); + strcpy(name, default_name); else - strncpy(name, cfg->name, name_sz); + strcpy(name, cfg->name); return name; } diff --git a/libvmaf/src/read_json_model.c b/libvmaf/src/read_json_model.c index 4022b8e76..7598a82b6 100644 --- a/libvmaf/src/read_json_model.c +++ b/libvmaf/src/read_json_model.c @@ -62,6 +62,13 @@ static int parse_feature_opts_dicts(json_stream *s, VmafModel *model) } free(key); if (err) return err; + } else if (json_peek(s) == JSON_STRING) { + const char *val = json_get_string(s, NULL); + const uint64_t flags = VMAF_DICT_DO_NOT_OVERWRITE; + int err = vmaf_dictionary_set(&(model->feature[i].opts_dict), + key, val, flags); + free(key); + if (err) return err; } else { return -EINVAL; //TODO } diff --git a/libvmaf/src/svm.cpp b/libvmaf/src/svm.cpp index 24175c9a8..94d3379d8 100644 --- a/libvmaf/src/svm.cpp +++ b/libvmaf/src/svm.cpp @@ -54,7 +54,7 @@ static void info(const char *fmt,...) char buf[BUFSIZ]; va_list ap; va_start(ap,fmt); - vsprintf(buf,fmt,ap); + vsnprintf(buf,BUFSIZ,fmt,ap); va_end(ap); (*svm_print_string)(buf); } diff --git a/libvmaf/test/meson.build b/libvmaf/test/meson.build index 250272035..8ae58e512 100644 --- a/libvmaf/test/meson.build +++ b/libvmaf/test/meson.build @@ -122,6 +122,15 @@ test_psnr = executable('test_psnr', link_with : get_option('default_library') == 'both' ? libvmaf.get_static_lib() : libvmaf, ) +test_framesync = executable('test_framesync', + ['test.c', 'test_framesync.c'], + include_directories : [libvmaf_inc, test_inc, include_directories('../src/')], + link_with : get_option('default_library') == 'both' ? libvmaf.get_static_lib() : libvmaf, + c_args : vmaf_cflags_common, + cpp_args : vmaf_cflags_common, + dependencies : thread_lib, +) + if get_option('enable_cuda') test_ring_buffer = executable('test_ring_buffer', ['test.c', 'test_ring_buffer.c', '../src/cuda/ring_buffer.c', '../src/cuda/picture_cuda.c'], @@ -158,3 +167,4 @@ test('test_cambi', test_cambi) test('test_luminance_tools', test_luminance_tools) test('test_cli_parse', test_cli_parse) test('test_psnr', test_psnr) +test('test_framesync', test_framesync) diff --git a/libvmaf/test/test_cambi.c b/libvmaf/test/test_cambi.c index def3caf5a..5318b281c 100644 --- a/libvmaf/test/test_cambi.c +++ b/libvmaf/test/test_cambi.c @@ -339,6 +339,7 @@ static char *test_get_spatial_mask_for_index() // dp_height = 2 * (filter_size >> 2) + 2 uint32_t mask_dp[7*4]; int err = 0; + uint16_t derivative_buffer[4]; err |= get_sample_image(&image, 3); mu_assert("test_get_spatial_mask_for_index alloc #1 error", !err); @@ -346,11 +347,11 @@ static char *test_get_spatial_mask_for_index() err |= get_sample_image(&mask, 3); mu_assert("test_get_spatial_mask_for_index alloc #2 error", !err); - get_spatial_mask_for_index(&image, &mask, mask_dp, 2, filter_size, width, height); + get_spatial_mask_for_index(&image, &mask, mask_dp, derivative_buffer, 2, filter_size, width, height, get_derivative_data_for_row); mu_assert("spatial_mask_for_index wrong mask for index=2, image=3", data_pic_sum(&mask)==14); - get_spatial_mask_for_index(&image, &mask, mask_dp, 1, filter_size, width, height); + get_spatial_mask_for_index(&image, &mask, mask_dp, derivative_buffer, 1, filter_size, width, height, get_derivative_data_for_row); mu_assert("spatial_mask_for_index wrong mask for index=1, image=3", data_pic_sum(&mask)==16); - get_spatial_mask_for_index(&image, &mask, mask_dp, 0, filter_size, width, height); + get_spatial_mask_for_index(&image, &mask, mask_dp, derivative_buffer, 0, filter_size, width, height, get_derivative_data_for_row); mu_assert("spatial_mask_for_index wrong mask for index=0, image=3", data_pic_sum(&mask)==16); vmaf_picture_unref(&image); @@ -358,11 +359,11 @@ static char *test_get_spatial_mask_for_index() err |= get_sample_image(&image, 4); mu_assert("test_get_spatial_mask_for_index alloc #3 error", !err); - get_spatial_mask_for_index(&image, &mask, mask_dp, 3, filter_size, width, height); + get_spatial_mask_for_index(&image, &mask, mask_dp, derivative_buffer, 3, filter_size, width, height, get_derivative_data_for_row); mu_assert("spatial_mask_for_index wrong mask for index=3, image=4", data_pic_sum(&mask)==0); - get_spatial_mask_for_index(&image, &mask, mask_dp, 2, filter_size, width, height); + get_spatial_mask_for_index(&image, &mask, mask_dp, derivative_buffer, 2, filter_size, width, height, get_derivative_data_for_row); mu_assert("spatial_mask_for_index wrong mask for index=2, image=4", data_pic_sum(&mask)==6); - get_spatial_mask_for_index(&image, &mask, mask_dp, 1, filter_size, width, height); + get_spatial_mask_for_index(&image, &mask, mask_dp, derivative_buffer, 1, filter_size, width, height, get_derivative_data_for_row); mu_assert("spatial_mask_for_index wrong mask for index=1, image=4", data_pic_sum(&mask)==9); vmaf_picture_unref(&image); diff --git a/libvmaf/test/test_cli_parse.c b/libvmaf/test/test_cli_parse.c index a651eb103..7ba87babe 100644 --- a/libvmaf/test/test_cli_parse.c +++ b/libvmaf/test/test_cli_parse.c @@ -23,8 +23,11 @@ #include "cli_parse.h" static int cli_free_dicts(CLISettings *settings) { - for (int i = 0; i < settings->feature_cnt; i++) - vmaf_feature_dictionary_free(&(settings->feature_cfg[i].opts_dict)); + for (unsigned i = 0; i < settings->feature_cnt; i++) { + int err = vmaf_feature_dictionary_free(&(settings->feature_cfg[i].opts_dict)); + if (err) return err; + } + return 0; } static char *test_aom_ctc_v1_0() diff --git a/libvmaf/test/test_framesync.c b/libvmaf/test/test_framesync.c new file mode 100644 index 000000000..f5e9997cc --- /dev/null +++ b/libvmaf/test/test_framesync.c @@ -0,0 +1,150 @@ +/** + * + * Copyright 2016-2020 Netflix, Inc. + * + * Licensed under the BSD+Patent License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSDplusPatent + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#ifdef _WIN32 +#include +#else +#include +#endif + +#include "framesync.h" +#include "test.h" +#include "thread_pool.h" + +#define NUM_TEST_FRAMES 10 +#define FRAME_BUF_LEN 1024 + +typedef struct ThreadData { + uint8_t *ref; + uint8_t *dist; + unsigned index; + VmafFrameSyncContext *fs_ctx; + int err; +} ThreadData; + +static void my_worker(void *data) +{ + int ctr; + struct ThreadData *thread_data = data; + uint8_t *shared_buf; + uint8_t *dependent_buf; + + //acquire new buffer from frame sync + vmaf_framesync_acquire_new_buf(thread_data->fs_ctx, (void*)&shared_buf, + FRAME_BUF_LEN, thread_data->index); + + //populate shared buffer with values + for (ctr = 0; ctr < FRAME_BUF_LEN; ctr++) + shared_buf[ctr] = thread_data->ref[ctr] + thread_data->dist[ctr] + 2; + + //submit filled buffer back to frame sync + vmaf_framesync_submit_filled_data(thread_data->fs_ctx, shared_buf, + thread_data->index); + + //sleep to simulate work load + const int sleep_seconds = 1; +#ifdef _WIN32 + Sleep(1000 * sleep_seconds); +#else + sleep(sleep_seconds); +#endif + + if (thread_data->index == 0) goto cleanup; + + //retrieve dependent buffer from frame sync + vmaf_framesync_retrieve_filled_data(thread_data->fs_ctx, + (void*)&dependent_buf, + thread_data->index - 1); + + for (ctr = 0; ctr < FRAME_BUF_LEN; ctr++) { + if (dependent_buf[ctr] != (thread_data->ref[ctr] + thread_data->dist[ctr])) { + fprintf(stderr, "verification error in frame index %d\n", + thread_data->index); + } + } + + //release dependent buffer from frame sync + vmaf_framesync_release_buf(thread_data->fs_ctx, dependent_buf, + thread_data->index - 1); + +cleanup: + free(thread_data->ref); + free(thread_data->dist); +} + +static char *test_framesync_create_process_and_destroy() +{ + int err, frame_index; + + VmafThreadPool *pool; + VmafFrameSyncContext *fs_ctx; + unsigned n_threads = 2; + + err = vmaf_thread_pool_create(&pool, n_threads); + mu_assert("problem during vmaf_thread_pool_init", !err); + + err = vmaf_framesync_init(&fs_ctx); + mu_assert("problem during vmaf_framesync_init", !err); + + fprintf(stderr, "\n"); + for (frame_index = 0; frame_index < NUM_TEST_FRAMES; frame_index++) { + uint8_t *pic_a = malloc(FRAME_BUF_LEN); + uint8_t *pic_b = malloc(FRAME_BUF_LEN); + + fprintf(stderr, "processing frame %d\r", frame_index); + + memset(pic_a, frame_index, FRAME_BUF_LEN); + memset(pic_b, frame_index, FRAME_BUF_LEN); + + struct ThreadData data = { + .ref = pic_a, + .dist = pic_b, + .index = frame_index, + .fs_ctx = fs_ctx, + .err = 0, + }; + + err = vmaf_thread_pool_enqueue(pool, my_worker, &data, sizeof(ThreadData)); + + mu_assert("problem during vmaf_thread_pool_enqueue with data", !err); + + //wait once in 2 frames + if ((frame_index >= 1) && (frame_index & 1)) { + err = vmaf_thread_pool_wait(pool); + mu_assert("problem during vmaf_thread_pool_wait", !err); + } + } + fprintf(stderr, "\n"); + + err = vmaf_thread_pool_wait(pool); + mu_assert("problem during vmaf_thread_pool_wait\n", !err); + err = vmaf_thread_pool_destroy(pool); + mu_assert("problem during vmaf_thread_pool_destroy\n", !err); + err = vmaf_framesync_destroy(fs_ctx); + mu_assert("problem during vmaf_framesync_destroy\n", !err); + + return NULL; +} + +char *run_tests() +{ + mu_run_test(test_framesync_create_process_and_destroy); + return NULL; +} diff --git a/python/pyproject.toml b/python/pyproject.toml new file mode 100644 index 000000000..83de03ad5 --- /dev/null +++ b/python/pyproject.toml @@ -0,0 +1,2 @@ +[build-system] +requires = ["setuptools", "wheel", "Cython", "numpy"] diff --git a/python/requirements.txt b/python/requirements.txt index 05f020292..e6a249bf9 100644 --- a/python/requirements.txt +++ b/python/requirements.txt @@ -7,7 +7,6 @@ scikit-image>=0.16.2 h5py>=2.6.0 sureal>=0.4.2 dill>=0.3.1 -cython -PyWavelets==1.1.1 +PyWavelets>=1.1.1 python-slugify>=5.0.0 libsvm-official>=3.30 diff --git a/python/setup.py b/python/setup.py index cd559c8c2..bb4bf3406 100755 --- a/python/setup.py +++ b/python/setup.py @@ -9,9 +9,8 @@ """ import os -from distutils.core import setup -from Cython.Build import cythonize -import numpy + +from setuptools import setup PYTHON_PROJECT = os.path.dirname(os.path.abspath(__file__)) @@ -24,12 +23,38 @@ def get_version(): for line in fh: if line.startswith("__version__"): return line.strip().rpartition(" ")[2].replace('"', "") + except Exception: pass + return "0.0-dev" -ext_module = cythonize(['vmaf/core/adm_dwt2_cy.pyx']) -ext_module[0].include_dirs = [numpy.get_include(), '../libvmaf/src'] + +class LazyExtensions(list): + _extensions = None + + @property + def extensions(self): + if self._extensions is None: + from Cython.Build import cythonize + import numpy + + self._extensions = cythonize([ + 'vmaf/core/adm_dwt2_cy.pyx' + ], compiler_directives={'language_level' : "3"}) + self._extensions[0].include_dirs = [numpy.get_include(), '../libvmaf/src'] + + return self._extensions + + def __iter__(self): + return iter(self.extensions) + + def __contains__(self, value): + return value in self.extensions + + def __len__(self): + return len(self.extensions) + setup( name="vmaf", @@ -66,5 +91,5 @@ def get_version(): 'run_vmaf_training=vmaf.script.run_vmaf_training:main', ], }, - ext_modules=ext_module, + ext_modules=LazyExtensions(), ) diff --git a/python/test/cy_test.py b/python/test/cy_test.py index 983f95e8a..63a0c2435 100644 --- a/python/test/cy_test.py +++ b/python/test/cy_test.py @@ -87,15 +87,15 @@ def test_adm_dwt2_cy_xsmallP_dc(self): self.assertEqual(h.shape, (9, 11)) self.assertEqual(d.shape, (9, 11)) - self.assertAlmostEqual(float(np.max(a)), 109.99999999999997, places=16) - self.assertAlmostEqual(float(np.max(v)), 8.526512829121202e-14, places=16) - self.assertAlmostEqual(float(np.max(h)), 8.038873388460928e-14, places=16) - self.assertAlmostEqual(float(np.max(d)), 0.0, places=16) + self.assertAlmostEqual(float(np.max(a)), 110.0, places=12) + self.assertAlmostEqual(float(np.max(v)), 0.0, places=12) + self.assertAlmostEqual(float(np.max(h)), 0.0, places=12) + self.assertAlmostEqual(float(np.max(d)), 0.0, places=12) - self.assertAlmostEqual(float(np.min(a)), 109.99999999999997, places=16) - self.assertAlmostEqual(float(np.min(v)), 8.526512829121202e-14, places=16) - self.assertAlmostEqual(float(np.min(h)), 8.038873388460928e-14, places=16) - self.assertAlmostEqual(float(np.min(d)), 0.0, places=16) + self.assertAlmostEqual(float(np.min(a)), 110.0, places=12) + self.assertAlmostEqual(float(np.min(v)), 0.0, places=12) + self.assertAlmostEqual(float(np.min(h)), 0.0, places=12) + self.assertAlmostEqual(float(np.min(d)), 0.0, places=12) def test_adm_dwt2_cy_dc(self): x = (55 * np.ones([324, 576])).astype(np.float64) @@ -106,15 +106,15 @@ def test_adm_dwt2_cy_dc(self): self.assertEqual(h.shape, (162, 288)) self.assertEqual(d.shape, (162, 288)) - self.assertAlmostEqual(float(np.max(a)), 109.99999999999999, places=8) - self.assertAlmostEqual(float(np.max(v)), 8.526512829121202e-14, places=16) - self.assertAlmostEqual(float(np.max(h)), 8.038873388460928e-14, places=16) - self.assertAlmostEqual(float(np.max(d)), 0.0, places=16) + self.assertAlmostEqual(float(np.max(a)), 110.0, places=12) + self.assertAlmostEqual(float(np.max(v)), 0.0, places=12) + self.assertAlmostEqual(float(np.max(h)), 0.0, places=12) + self.assertAlmostEqual(float(np.max(d)), 0.0, places=12) - self.assertAlmostEqual(float(np.min(a)), 109.99999999999997, places=16) - self.assertAlmostEqual(float(np.min(v)), 8.526512829121202e-14, places=16) - self.assertAlmostEqual(float(np.min(h)), 8.038873388460928e-14, places=16) - self.assertAlmostEqual(float(np.min(d)), 0.0, places=16) + self.assertAlmostEqual(float(np.min(a)), 110.0, places=12) + self.assertAlmostEqual(float(np.min(v)), 0.0, places=12) + self.assertAlmostEqual(float(np.min(h)), 0.0, places=12) + self.assertAlmostEqual(float(np.min(d)), 0.0, places=12) class AdmDwt2CyTestOnAkiyo(unittest.TestCase): diff --git a/python/test/feature_extractor_test.py b/python/test/feature_extractor_test.py index 62b871667..fbdab5c26 100644 --- a/python/test/feature_extractor_test.py +++ b/python/test/feature_extractor_test.py @@ -81,7 +81,7 @@ def test_run_vmaf_fextractor(self): self.assertAlmostEqual(results[0]['VMAF_feature_vif_num_score'], 712650.023478, places=0) self.assertAlmostEqual(results[0]['VMAF_feature_vif_den_score'], 1597314.95249, places=0) - self.assertAlmostEqual(results[0]['VMAF_feature_adm_num_score'], 371.80645372916666, places=4) + self.assertAlmostEqual(results[0]['VMAF_feature_adm_num_score'], 371.80645372916666, places=3) self.assertAlmostEqual(results[0]['VMAF_feature_adm_den_score'], 397.83378972916671, places=4) self.assertAlmostEqual(results[0]['VMAF_feature_anpsnr_score'], 34.164776875, places=4) diff --git a/python/test/quality_runner_test.py b/python/test/quality_runner_test.py index 7771297d9..9c224f3dd 100644 --- a/python/test/quality_runner_test.py +++ b/python/test/quality_runner_test.py @@ -186,7 +186,7 @@ def test_run_vmaf_runner_v1_model(self): self.assertAlmostEqual(results[1]['VMAF_feature_adm_score'], 1.0, places=4) self.assertAlmostEqual(results[1]['VMAF_feature_ansnr_score'], 31.2714392708, places=4) - self.assertAlmostEqual(results[0]['VMAF_score'], 77.17414738991636, places=4) + self.assertAlmostEqual(results[0]['VMAF_score'], 77.17414738991636, places=3) self.assertAlmostEqual(results[1]['VMAF_score'], 100.0, places=4) def test_run_vmaf_runner(self): @@ -1045,7 +1045,7 @@ def test_run_bootstrap_vmaf_runner_residue_bootstrap_model(self): self.assertAlmostEqual(results[0]['BOOTSTRAP_VMAF_bagging_score'], 73.09131553704874, places=4) self.assertAlmostEqual(results[1]['BOOTSTRAP_VMAF_bagging_score'], 99.79000465995409, places=4) self.assertAlmostEqual(results[0]['BOOTSTRAP_VMAF_stddev_score'], 1.1982762081883995, places=4) - self.assertAlmostEqual(results[1]['BOOTSTRAP_VMAF_stddev_score'], 1.3028824838324222, places=4) + self.assertAlmostEqual(results[1]['BOOTSTRAP_VMAF_stddev_score'], 1.3028824838324222, places=3) self.assertAlmostEqual(results[0]['BOOTSTRAP_VMAF_ci95_low_score'], 70.81472328674501, places=4) self.assertAlmostEqual(results[1]['BOOTSTRAP_VMAF_ci95_low_score'], 94.79667446930989, places=4) self.assertAlmostEqual(results[0]['BOOTSTRAP_VMAF_ci95_high_score'], 74.83768715705374, places=4) @@ -1311,7 +1311,7 @@ def test_run_vmaf_runner_input160x90(self): with self.assertRaises(KeyError): self.assertAlmostEqual(results[1]['VMAF_integer_feature_motion_score'], 1.0, places=4) - self.assertAlmostEqual(results[0]['VMAF_score'], 92.52344867729687, places=3) + self.assertAlmostEqual(results[0]['VMAF_score'], 92.52344867729687, places=2) self.assertAlmostEqual(results[1]['VMAF_score'], 99.30930978456455, places=4) def test_run_vmaf_runner_json_model(self): @@ -1444,7 +1444,7 @@ def test_run_vmaf_runner_neg_mode(self): self.runner.run(parallelize=False) results = self.runner.results - self.assertAlmostEqual(results[0]['VMAFNEG_score'], 88.030463, places=4) # 132.7329528948058 + self.assertAlmostEqual(results[0]['VMAFNEG_score'], 88.030463, places=3) # 132.7329528948058 self.assertAlmostEqual(results[0]['VMAF_integer_feature_vif_scale0_score'], 0.9837379749630343, places=4) with self.assertRaises(KeyError): @@ -1536,7 +1536,7 @@ def test_run_vmaf_runner_float_nvd6(self): self.assertAlmostEqual(results[1]['VMAF_feature_motion2_score'], 3.8953518541666665, places=4) self.assertAlmostEqual(results[1]['VMAF_feature_adm2_score'], 1.0, places=4) - self.assertAlmostEqual(results[0]['VMAF_score'], 80.61670115719328, places=4) + self.assertAlmostEqual(results[0]['VMAF_score'], 80.61670115719328, places=3) self.assertAlmostEqual(results[1]['VMAF_score'], 99.946416604585025, places=4) def test_run_vmaf_runner_rdh540(self): @@ -1969,7 +1969,7 @@ def test_quality_runner_with_different_models(self): self.assertAlmostEqual(results1[0]['VMAF_score'], 73.28968543912883, places=4) self.assertAlmostEqual(results1[1]['VMAF_score'], 99.946416604585025, places=4) - self.assertAlmostEqual(results2[0]['VMAF_score'], 80.61670115719328, places=4) + self.assertAlmostEqual(results2[0]['VMAF_score'], 80.61670115719328, places=3) self.assertAlmostEqual(results2[1]['VMAF_score'], 99.946416604585025, places=4) diff --git a/python/test/routine_test.py b/python/test/routine_test.py index 575527096..80bfa3b33 100644 --- a/python/test/routine_test.py +++ b/python/test/routine_test.py @@ -360,7 +360,7 @@ def test_test_on_dataset(self): self.assertAlmostEqual(results[0]['VMAF_score'], 99.142659046424384, places=4) self.assertAlmostEqual(results[1]['VMAF_score'], 35.066157497128764, places=4) self.assertAlmostEqual(results[2]['VMAF_score'], 97.428042675471147, places=4) - self.assertAlmostEqual(results[3]['VMAF_score'], 97.427927701008869, places=4) + self.assertAlmostEqual(results[3]['VMAF_score'], 97.427927701008869, places=3) self.assertAlmostEqual(test_assets[0].groundtruth, 100, places=4) self.assertAlmostEqual(test_assets[1].groundtruth, 50, places=4) self.assertAlmostEqual(test_assets[2].groundtruth, 100, places=4) @@ -466,7 +466,7 @@ def test_test_on_dataset_raw(self): self.assertAlmostEqual(results[0]['VMAF_score'], 99.142659046424384, places=4) self.assertAlmostEqual(results[1]['VMAF_score'], 35.066157497128764, places=4) self.assertAlmostEqual(results[2]['VMAF_score'], 97.428042675471147, places=4) - self.assertAlmostEqual(results[3]['VMAF_score'], 97.427927701008869, places=4) + self.assertAlmostEqual(results[3]['VMAF_score'], 97.427927701008869, places=3) self.assertAlmostEqual(test_assets[0].groundtruth, 100, places=4) self.assertAlmostEqual(test_assets[1].groundtruth, 50, places=4) self.assertAlmostEqual(test_assets[2].groundtruth, 100, places=4) @@ -489,7 +489,7 @@ def test_test_on_dataset_mle(self): self.assertAlmostEqual(results[0]['VMAF_score'], 99.142659046424384, places=4) self.assertAlmostEqual(results[1]['VMAF_score'], 35.066157497128764, places=4) self.assertAlmostEqual(results[2]['VMAF_score'], 97.428042675471147, places=4) - self.assertAlmostEqual(results[3]['VMAF_score'], 97.427927701008869, places=4) + self.assertAlmostEqual(results[3]['VMAF_score'], 97.427927701008869, places=3) self.assertAlmostEqual(test_assets[0].groundtruth, 100, places=4) self.assertAlmostEqual(test_assets[1].groundtruth, 50, places=4) self.assertAlmostEqual(test_assets[2].groundtruth, 90, places=4) diff --git a/python/test/vmafexec_feature_extractor_test.py b/python/test/vmafexec_feature_extractor_test.py index 22c1a9af1..828980034 100644 --- a/python/test/vmafexec_feature_extractor_test.py +++ b/python/test/vmafexec_feature_extractor_test.py @@ -162,7 +162,7 @@ def test_run_float_vif_fextractor_with_debug(self): self.assertAlmostEqual(results[0]['float_VIF_feature_vif_scale0_score'], 0.3634208125, places=6) self.assertAlmostEqual(results[0]['float_VIF_feature_vif_scale1_score'], 0.7666474166666667, places=6) self.assertAlmostEqual(results[0]['float_VIF_feature_vif_scale2_score'], 0.8628533333333334, places=5) - self.assertAlmostEqual(results[0]['float_VIF_feature_vif_scale3_score'], 0.9159719583333334, places=6) + self.assertAlmostEqual(results[0]['float_VIF_feature_vif_scale3_score'], 0.9159719583333334, places=5) self.assertAlmostEqual(results[1]['float_VIF_feature_vif_scale0_score'], 1.0, places=5) self.assertAlmostEqual(results[1]['float_VIF_feature_vif_scale1_score'], 1.0, places=5) self.assertAlmostEqual(results[1]['float_VIF_feature_vif_scale2_score'], 1.0, places=5) @@ -349,13 +349,13 @@ def test_run_float_adm_fextractor_with_debug(self): self.assertAlmostEqual(results[0]['float_ADM_feature_adm_scale3_score'], 0.9649663541666667, places=4) self.assertAlmostEqual(results[0]['float_ADM_feature_adm_score'], 0.9345148541666667, places=4) - self.assertAlmostEqual(results[0]['float_ADM_feature_adm_num_score'], 371.80645372916666, places=4) + self.assertAlmostEqual(results[0]['float_ADM_feature_adm_num_score'], 371.80645372916666, places=3) self.assertAlmostEqual(results[0]['float_ADM_feature_adm_den_score'], 397.83379106250004, places=4) self.assertAlmostEqual(results[0]['float_ADM_feature_adm_num_scale0_score'], 45.526146958333335, places=4) self.assertAlmostEqual(results[0]['float_ADM_feature_adm_den_scale0_score'], 50.14385129166667, places=4) self.assertAlmostEqual(results[0]['float_ADM_feature_adm_num_scale1_score'], 66.574236, places=4) self.assertAlmostEqual(results[0]['float_ADM_feature_adm_den_scale1_score'], 74.47438383333333, places=4) - self.assertAlmostEqual(results[0]['float_ADM_feature_adm_num_scale2_score'], 105.55483329166668, places=4) + self.assertAlmostEqual(results[0]['float_ADM_feature_adm_num_scale2_score'], 105.55483329166668, places=3) self.assertAlmostEqual(results[0]['float_ADM_feature_adm_den_scale2_score'], 113.49725864583333, places=4) self.assertAlmostEqual(results[0]['float_ADM_feature_adm_num_scale3_score'], 154.15123754166666, places=4) self.assertAlmostEqual(results[0]['float_ADM_feature_adm_den_scale3_score'], 159.7182974375, places=4) @@ -476,7 +476,7 @@ def test_run_float_vif_fextractor_akiyo_multiply(self): ) self.fextractor.run(parallelize=True) results = self.fextractor.results - self.assertAlmostEqual(results[0]['float_VIF_feature_vif_scale0_score'], 1.0522544319369052, places=5) + self.assertAlmostEqual(results[0]['float_VIF_feature_vif_scale0_score'], 1.0522544319369052, places=4) self.assertAlmostEqual(results[0]['float_VIF_feature_vif_scale1_score'], 1.0705609423182443, places=5) self.assertAlmostEqual(results[0]['float_VIF_feature_vif_scale2_score'], 1.0731529493098957, places=4) self.assertAlmostEqual(results[0]['float_VIF_feature_vif_scale3_score'], 1.0728060231246508, places=4) diff --git a/python/test/vmafexec_test.py b/python/test/vmafexec_test.py index fb0c5cf75..7244a15bd 100644 --- a/python/test/vmafexec_test.py +++ b/python/test/vmafexec_test.py @@ -639,7 +639,7 @@ def test_run_vmafexec_runner_akiyo_multiply(self): self.assertAlmostEqual(results[0]['VMAFEXEC_vif_scale2_score'], 1.072518, places=4) # 1.0731529493098957 self.assertAlmostEqual(results[0]['VMAFEXEC_vif_scale3_score'], 1.072512, places=4) # 1.0728060231246508 - self.assertAlmostEqual(results[0]['VMAFEXEC_score'], 132.732952, places=3) # 132.78849246495625 + self.assertAlmostEqual(results[0]['VMAFEXEC_score'], 132.732952, places=2) # 132.78849246495625 def test_run_vmafexec_runner_akiyo_multiply_with_feature_enhn_gain_limit(self): ref_path = VmafConfig.test_resource_path("yuv", "refp_vmaf_hacking_investigation_0_0_akiyo_cif_notyuv_0to0_identity_vs_akiyo_cif_notyuv_0to0_multiply_q_352x288") @@ -667,7 +667,7 @@ def test_run_vmafexec_runner_akiyo_multiply_with_feature_enhn_gain_limit(self): self.assertAlmostEqual(results[0]['VMAFEXEC_vif_scale2_egl_1_score'], 0.9984692380091739, places=4) # 1.0731529493098957 self.assertAlmostEqual(results[0]['VMAFEXEC_vif_scale3_egl_1_score'], 0.999146211879154, places=4) # 1.0728060231246508 - self.assertAlmostEqual(results[0]['VMAFEXEC_score'], 88.030463, places=4) # 132.78849246495625 + self.assertAlmostEqual(results[0]['VMAFEXEC_score'], 88.030463, places=3) # 132.78849246495625 def test_run_vmafexec_runner_akiyo_multiply_with_feature_enhn_gain_limit_custom(self): ref_path = VmafConfig.test_resource_path("yuv", "refp_vmaf_hacking_investigation_0_0_akiyo_cif_notyuv_0to0_identity_vs_akiyo_cif_notyuv_0to0_multiply_q_352x288") @@ -695,7 +695,7 @@ def test_run_vmafexec_runner_akiyo_multiply_with_feature_enhn_gain_limit_custom( self.assertAlmostEqual(results[0]['VMAFEXEC_vif_scale2_egl_1.1_score'], 1.04852, places=4) # 1.0731529493098957 self.assertAlmostEqual(results[0]['VMAFEXEC_vif_scale3_egl_1.1_score'], 1.04892, places=4) # 1.0728060231246508 - self.assertAlmostEqual(results[0]['VMAFEXEC_score'], 129.474226, places=3) # 132.78849246495625 + self.assertAlmostEqual(results[0]['VMAFEXEC_score'], 129.474226, places=2) # 132.78849246495625 def test_run_vmafexec_runner_akiyo_multiply_disable_enhn_gain(self): ref_path = VmafConfig.test_resource_path("yuv", "refp_vmaf_hacking_investigation_0_0_akiyo_cif_notyuv_0to0_identity_vs_akiyo_cif_notyuv_0to0_multiply_q_352x288") @@ -723,7 +723,7 @@ def test_run_vmafexec_runner_akiyo_multiply_disable_enhn_gain(self): self.assertAlmostEqual(results[0]['VMAFEXEC_vif_scale2_egl_1_score'], 0.9984692380091739, places=4) # 1.0731529493098957 self.assertAlmostEqual(results[0]['VMAFEXEC_vif_scale3_egl_1_score'], 0.999146211879154, places=4) # 1.0728060231246508 - self.assertAlmostEqual(results[0]['VMAFEXEC_score'], 88.030463, places=4) # 132.78849246495625 + self.assertAlmostEqual(results[0]['VMAFEXEC_score'], 88.030463, places=3) # 132.78849246495625 def test_run_vmafexec_runner_akiyo_multiply_no_enhn_gain_model(self): ref_path = VmafConfig.test_resource_path("yuv", "refp_vmaf_hacking_investigation_0_0_akiyo_cif_notyuv_0to0_identity_vs_akiyo_cif_notyuv_0to0_multiply_q_352x288") @@ -751,7 +751,7 @@ def test_run_vmafexec_runner_akiyo_multiply_no_enhn_gain_model(self): self.assertAlmostEqual(results[0]['VMAFEXEC_vif_scale2_egl_1_score'], 0.9984692380091739, places=4) # 1.0731529493098957 self.assertAlmostEqual(results[0]['VMAFEXEC_vif_scale3_egl_1_score'], 0.999146211879154, places=4) # 1.0728060231246508 - self.assertAlmostEqual(results[0]['VMAFEXEC_score'], 88.030463, places=4) # 132.78849246495625 + self.assertAlmostEqual(results[0]['VMAFEXEC_score'], 88.030463, places=3) # 132.78849246495625 def test_run_vmafexec_runner_akiyo_multiply_no_enhn_gain_model_and_cmd_options(self): ref_path = VmafConfig.test_resource_path("yuv", "refp_vmaf_hacking_investigation_0_0_akiyo_cif_notyuv_0to0_identity_vs_akiyo_cif_notyuv_0to0_multiply_q_352x288") @@ -780,7 +780,7 @@ def test_run_vmafexec_runner_akiyo_multiply_no_enhn_gain_model_and_cmd_options(s self.assertAlmostEqual(results[0]['VMAFEXEC_vif_scale2_egl_1_score'], 0.9984692380091739, places=4) # 1.0731529493098957 self.assertAlmostEqual(results[0]['VMAFEXEC_vif_scale3_egl_1_score'], 0.999146211879154, places=4) # 1.0728060231246508 - self.assertAlmostEqual(results[0]['VMAFEXEC_score'], 122.804272, places=3) # 132.78849246495625 + self.assertAlmostEqual(results[0]['VMAFEXEC_score'], 122.804272, places=2) # 132.78849246495625 def test_run_vmafexec_runner_akiyo_multiply_no_enhn_gain_model_and_cmd_options_illegal(self): ref_path = VmafConfig.test_resource_path("yuv", "refp_vmaf_hacking_investigation_0_0_akiyo_cif_notyuv_0to0_identity_vs_akiyo_cif_notyuv_0to0_multiply_q_352x288") diff --git a/python/tox.ini b/python/tox.ini index cdd98a8dc..4f15faba2 100644 --- a/python/tox.ini +++ b/python/tox.ini @@ -1,10 +1,13 @@ [tox] envlist = py38, coverage -skip_missing_interpreters = true [testenv] -passenv = APPVEYOR* CI SSL_CERT_FILE TEST_MARKER TRAVIS* +passenv = APPVEYOR* + CI + SSL_CERT_FILE + TEST_MARKER + TRAVIS* setenv = COVERAGE_FILE={toxworkdir}/.coverage.{envname} usedevelop = True deps = -rrequirements.txt diff --git a/python/vmaf/core/niqe_train_test_model.py b/python/vmaf/core/niqe_train_test_model.py index 772949e96..05a0a0587 100644 --- a/python/vmaf/core/niqe_train_test_model.py +++ b/python/vmaf/core/niqe_train_test_model.py @@ -64,9 +64,9 @@ def train(self, xys): xs_2d = [] for i_sample in range(num_samples): - xs_2d_ = np.vstack(map( + xs_2d_ = np.vstack(list(map( lambda feature_name: xys[feature_name][i_sample], feature_names) - ).T + )).T xs_2d.append(xs_2d_) xs_2d = np.vstack(xs_2d) diff --git a/resource/doc/aom_ctc.md b/resource/doc/aom_ctc.md index eb43fe9ca..863ceeae8 100644 --- a/resource/doc/aom_ctc.md +++ b/resource/doc/aom_ctc.md @@ -55,3 +55,15 @@ There are also a few optional command-line settings you may find useful. * Add CAMBI * Release: [libvmaf v2.3.1](https://github.com/Netflix/vmaf/releases/tag/v2.3.1) * Precompiled static binaries [here](https://github.com/Netflix/vmaf/releases/tag/v2.3.1) + +* v4.0: `--aom_ctc v4.0` + * Identical to `v3.0` + +* v5.0: `--aom_ctc v5.0` + * Identical to `v4.0` + +* v6.0: `--aom_ctc v6.0` + * 2023-12-07 + * Support bit depth conversion for Y4M inputs + * Release: [libvmaf v3.0.0](https://github.com/Netflix/vmaf/releases/tag/v3.0.0) + * Precompiled static binaries [here](https://github.com/Netflix/vmaf/releases/tag/v3.0.0) \ No newline at end of file diff --git a/resource/doc/python.md b/resource/doc/python.md index 683b05d57..e0a7c6b48 100644 --- a/resource/doc/python.md +++ b/resource/doc/python.md @@ -41,7 +41,7 @@ brew install python3 Install the remaining dependencies: ```bash -brew install nasm doxygen +brew install nasm doxygen llvm ``` Note that `brew` requires no `sudo`. @@ -76,6 +76,13 @@ Install the rest of the required Python packages: pip3 install -r python/requirements.txt ``` +On macOS it's important to use the LLVM from homebrew as the macOS clang does not include support for OpenMP, which is needed for libsvm-official + +``` shell script +LLVM_CONFIG=$HOMEBREW_PREFIX/opt/llvm/bin/llvm-config pip install -r ./python/requirements.txt +``` + + ## Testing Run unittests and make sure they all pass: