<commit_before>// Copyright 2008-present Contributors to the OpenImageIO project.
// SPDX-License-Identifier: BSD-3-Clause
// https://github.com/OpenImageIO/oiio


#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <iterator>
#include <memory>
#include <regex>

#include <OpenImageIO/argparse.h>
#include <OpenImageIO/deepdata.h>
#include <OpenImageIO/filesystem.h>
#include <OpenImageIO/hash.h>
#include <OpenImageIO/imagebuf.h>
#include <OpenImageIO/imagebufalgo.h>
#include <OpenImageIO/imageio.h>
#include <OpenImageIO/strutil.h>
#include <OpenImageIO/sysutil.h>

using namespace OIIO;
using namespace ImageBufAlgo;


static bool verbose = false;
static bool sum     = false;
static bool help    = false;
static std::vector<std::string> filenames;
static std::string metamatch;
static bool filenameprefix = false;
static std::regex field_re;
static bool subimages     = false;
static bool compute_sha1  = false;
static bool compute_stats = false;



static void
print_sha1(ImageInput* input, int subimage, int miplevel)
{
    using Strutil::print;
    SHA1 sha;
    ImageSpec spec = input->spec_dimensions(subimage, miplevel);
    if (spec.deep) {
        // Special handling of deep data
        DeepData dd;
        if (!input->read_native_deep_image(subimage, miplevel, dd)) {
            std::string err = input->geterror();
            if (err.empty())
                err = "could not read image";
            print("    SHA-1: {}\n", err);
            return;
        }
        // Hash both the sample counts and the data block
        sha.append(dd.all_samples());
        sha.append(dd.all_data());
    } else {
        imagesize_t size = spec.image_bytes(true /*native*/);
        if (size >= std::numeric_limits<size_t>::max()) {
            print("    SHA-1: unable to compute, image is too big\n");
            return;
        }
        std::unique_ptr<char[]> buf(new char[size]);
        if (!input->read_image(subimage, miplevel, 0, spec.nchannels,
                               TypeUnknown /*native*/, buf.get())) {
            std::string err = input->geterror();
            if (err.empty())
                err = "could not read image";
            print("    SHA-1: {}\n", err);
            return;
        }
        sha.append(&buf[0], size);
    }

    print("    SHA-1: {}\n", sha.digest());
}



///////////////////////////////////////////////////////////////////////////////
// Stats

static bool
read_input(const std::string& filename, ImageBuf& img, int subimage = 0,
           int miplevel = 0)
{
    if (img.subimage() >= 0 && img.subimage() == subimage)
        return true;

    if (img.read(subimage, miplevel, false, TypeDesc::FLOAT))
        return true;

    std::cerr << "iinfo ERROR: Could not read " << filename << ":\n\t"
              << img.geterror() << "\n";
    return false;
}



static void
print_stats_num(float val, int maxval, bool round)
{
    if (maxval == 0) {
        printf("%f", val);
    } else {
        float fval = val * static_cast<float>(maxval);
        if (round) {
            int v = static_cast<int>(roundf(fval));
            printf("%d", v);
        } else {
            printf("%0.2f", fval);
        }
    }
}


// First check oiio:BitsPerSample int attribute.  If not set,
// fall back on the TypeDesc. return 0 for float types
// or those that exceed the int range (long long, etc)
static unsigned long long
get_intsample_maxval(const ImageSpec& spec)
{
    TypeDesc type = spec.format;
    int bits      = spec.get_int_attribute("oiio:BitsPerSample");
    if (bits > 0) {
        if (type.basetype == TypeDesc::UINT8
            || type.basetype == TypeDesc::UINT16
            || type.basetype == TypeDesc::UINT32)
            return ((1LL) << bits) - 1;
        if (type.basetype == TypeDesc::INT8 || type.basetype == TypeDesc::INT16
            || type.basetype == TypeDesc::INT32)
            return ((1LL) << (bits - 1)) - 1;
    }

    // These correspond to all the int enums in typedesc.h <= int
    if (type.basetype == TypeDesc::UCHAR)
        return 0xff;
    if (type.basetype == TypeDesc::CHAR)
        return 0x7f;
    if (type.basetype == TypeDesc::USHORT)
        return 0xffff;
    if (type.basetype == TypeDesc::SHORT)
        return 0x7fff;
    if (type.basetype == TypeDesc::UINT)
        return 0xffffffff;
    if (type.basetype == TypeDesc::INT)
        return 0x7fffffff;

    return 0;
}


static void
print_stats_footer(unsigned int maxval)
{
    if (maxval == 0)
        printf("(float)");
    else
        printf("(of %u)", maxval);
}


static void
print_stats(const std::string& filename, const ImageSpec& originalspec,
            int subimage = 0, int miplevel = 0, bool indentmip = false)
{
    const char* indent = indentmip ? "      " : "    ";

    ImageBuf input;
    if (!read_input(filename, input, subimage, miplevel)) {
        // Note: read_input prints an error message if one occurs
        return;
    }

    PixelStats stats = computePixelStats(input);
    if (!stats.min.size()) {
        printf("%sStats: (unable to compute)\n", indent);
        if (input.has_error())
            std::cerr << "Error: " << input.geterror() << "\n";
        return;
    }

    // The original spec is used, otherwise the bit depth will
    // be reported incorrectly (as FLOAT)
    unsigned int maxval = (unsigned int)get_intsample_maxval(originalspec);

    printf("%sStats Min: ", indent);
    for (unsigned int i = 0; i < stats.min.size(); ++i) {
        print_stats_num(stats.min[i], maxval, true);
        printf(" ");
    }
    print_stats_footer(maxval);
    printf("\n");

    printf("%sStats Max: ", indent);
    for (unsigned int i = 0; i < stats.max.size(); ++i) {
        print_stats_num(stats.max[i], maxval, true);
        printf(" ");<commit_msg>Fix broken iinfo --stats (#3352)<commit_after>// Copyright 2008-present Contributors to the OpenImageIO project.
// SPDX-License-Identifier: BSD-3-Clause
// https://github.com/OpenImageIO/oiio


#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <iterator>
#include <memory>
#include <regex>

#include <OpenImageIO/argparse.h>
#include <OpenImageIO/deepdata.h>
#include <OpenImageIO/filesystem.h>
#include <OpenImageIO/hash.h>
#include <OpenImageIO/imagebuf.h>
#include <OpenImageIO/imagebufalgo.h>
#include <OpenImageIO/imageio.h>
#include <OpenImageIO/strutil.h>
#include <OpenImageIO/sysutil.h>

using namespace OIIO;
using namespace ImageBufAlgo;


static bool verbose = false;
static bool sum     = false;
static bool help    = false;
static std::vector<std::string> filenames;
static std::string metamatch;
static bool filenameprefix = false;
static std::regex field_re;
static bool subimages     = false;
static bool compute_sha1  = false;
static bool compute_stats = false;



static void
print_sha1(ImageInput* input, int subimage, int miplevel)
{
    using Strutil::print;
    SHA1 sha;
    ImageSpec spec = input->spec_dimensions(subimage, miplevel);
    if (spec.deep) {
        // Special handling of deep data
        DeepData dd;
        if (!input->read_native_deep_image(subimage, miplevel, dd)) {
            std::string err = input->geterror();
            if (err.empty())
                err = "could not read image";
            print("    SHA-1: {}\n", err);
            return;
        }
        // Hash both the sample counts and the data block
        sha.append(dd.all_samples());
        sha.append(dd.all_data());
    } else {
        imagesize_t size = spec.image_bytes(true /*native*/);
        if (size >= std::numeric_limits<size_t>::max()) {
            print("    SHA-1: unable to compute, image is too big\n");
            return;
        }
        std::unique_ptr<char[]> buf(new char[size]);
        if (!input->read_image(subimage, miplevel, 0, spec.nchannels,
                               TypeUnknown /*native*/, buf.get())) {
            std::string err = input->geterror();
            if (err.empty())
                err = "could not read image";
            print("    SHA-1: {}\n", err);
            return;
        }
        sha.append(&buf[0], size);
    }

    print("    SHA-1: {}\n", sha.digest());
}



///////////////////////////////////////////////////////////////////////////////
// Stats

static bool
read_input(const std::string& filename, ImageBuf& img, int subimage = 0,
           int miplevel = 0)
{
    if (img.subimage() >= 0 && img.subimage() == subimage)
        return true;

    if (img.read(subimage, miplevel, false, TypeDesc::FLOAT))
        return true;

    std::cerr << "iinfo ERROR: Could not read " << filename << ":\n\t"
              << img.geterror() << "\n";
    return false;
}



static void
print_stats_num(float val, int maxval, bool round)
{
    if (maxval == 0) {
        printf("%f", val);
    } else {
        float fval = val * static_cast<float>(maxval);
        if (round) {
            int v = static_cast<int>(roundf(fval));
            printf("%d", v);
        } else {
            printf("%0.2f", fval);
        }
    }
}


// First check oiio:BitsPerSample int attribute.  If not set,
// fall back on the TypeDesc. return 0 for float types
// or those that exceed the int range (long long, etc)
static unsigned long long
get_intsample_maxval(const ImageSpec& spec)
{
    TypeDesc type = spec.format;
    int bits      = spec.get_int_attribute("oiio:BitsPerSample");
    if (bits > 0) {
        if (type.basetype == TypeDesc::UINT8
            || type.basetype == TypeDesc::UINT16
            || type.basetype == TypeDesc::UINT32)
            return ((1LL) << bits) - 1;
        if (type.basetype == TypeDesc::INT8 || type.basetype == TypeDesc::INT16
            || type.basetype == TypeDesc::INT32)
            return ((1LL) << (bits - 1)) - 1;
    }

    // These correspond to all the int enums in typedesc.h <= int
    if (type.basetype == TypeDesc::UCHAR)
        return 0xff;
    if (type.basetype == TypeDesc::CHAR)
        return 0x7f;
    if (type.basetype == TypeDesc::USHORT)
        return 0xffff;
    if (type.basetype == TypeDesc::SHORT)
        return 0x7fff;
    if (type.basetype == TypeDesc::UINT)
        return 0xffffffff;
    if (type.basetype == TypeDesc::INT)
        return 0x7fffffff;

    return 0;
}


static void
print_stats_footer(unsigned int maxval)
{
    if (maxval == 0)
        printf("(float)");
    else
        printf("(of %u)", maxval);
}


static void
print_stats(const std::string& filename, const ImageSpec& originalspec,
            int subimage = 0, int miplevel = 0, bool indentmip = false)
{
    const char* indent = indentmip ? "      " : "    ";

    ImageBuf input(filename);
    if (!read_input(filename, input, subimage, miplevel)) {
        // Note: read_input prints an error message if one occurs
        return;
    }

    PixelStats stats = computePixelStats(input);
    if (!stats.min.size()) {
        printf("%sStats: (unable to compute)\n", indent);
        if (input.has_error())
            std::cerr << "Error: " << input.geterror() << "\n";
        return;
    }

    // The original spec is used, otherwise the bit depth will
    // be reported incorrectly (as FLOAT)
    unsigned int maxval = (unsigned int)get_intsample_maxval(originalspec);

    printf("%sStats Min: ", indent);
    for (unsigned int i = 0; i < stats.min.size(); ++i) {
        print_stats_num(stats.min[i], maxval, true);
        printf(" ");
    }
    print_stats_footer(maxval);
    printf("\n");

    printf("%sStats Max: ", indent);
    for (unsigned int i = 0; i < stats.max.size(); ++i) {
        print_stats_num(stats.max[i], maxval, true);
        printf(" ");
{'fraction_non_alphanumeric': 0.15076712093940173, 'fraction_numerical': 0.009256878374903575, 'mean_word_length': 2.383009567990722, 'pattern_counts': {'":': 2, '<': 93, '<?xml version=': 0, '>': 63, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 4, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5539588', 'n_tokens_mistral': 3775, 'n_tokens_neox': 3618, 'n_words': 984}