Source code for verl_omni.utils.reward_score.jpeg_compressibility

# Copyright 2026 Bytedance Ltd. and/or its affiliates
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# 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.

"""
The reward function for JPEG compressibility.
It is adapted from https://github.com/kvablack/ddpo-pytorch.
"""

import io

import numpy as np
import torch
from PIL import Image


def jpeg_incompressibility():
    def _fn(images, prompts):
        if isinstance(images, torch.Tensor):
            images = (images * 255).round().clamp(0, 255).to(torch.uint8).cpu().numpy()
            images = images.transpose(0, 2, 3, 1)  # NCHW -> NHWC
        images = [Image.fromarray(image) for image in images]
        buffers = [io.BytesIO() for _ in images]
        for image, buffer in zip(images, buffers, strict=False):
            image.save(buffer, format="JPEG", quality=95)
        sizes = [buffer.tell() / 1000 for buffer in buffers]
        return np.array(sizes), {}

    return _fn


def jpeg_compressibility():
    jpeg_fn = jpeg_incompressibility()

    def _fn(images, prompts):
        rew, meta = jpeg_fn(images, prompts)
        return -rew / 500, meta

    return _fn


[docs] def compute_score(solution_image): """The scoring function for JPEG compressibility. Args: solution_image: the solution image or video, in shape (C, H, W) or (N, C, H, W). """ if isinstance(solution_image, torch.Tensor) and solution_image.ndim == 3: solution_image = solution_image.unsqueeze(0) score = jpeg_compressibility()(solution_image, None)[0] return score