Skip to content

include/cvw/details/image_impl.hpp

Implementation details for Image template methods. More...

Namespaces

Name
cvw
cvw::detail
Internal validation helpers used by algorithm implementations.

Detailed Description

Implementation details for Image template methods.

Author: Charliechen114514

Version: 1.0.0

Since: 1.0.0

Date: 2026-05-15

Provides out-of-line definitions for Image constructors, accessors, view creation, clone, and compile-time format conversion. Also contains internal helpers for OpenCV type mapping and color code resolution.

Source code

cpp

#pragma once
#include "cvw/image.hpp"
#include "cvw/image_view.hpp"
#include <cstring>
#include <opencv2/imgproc.hpp>

namespace cvw {

namespace detail {

template <typename T> constexpr int cv_depth() {
    if constexpr (std::is_same_v<T, uint8_t>) {
        return CV_8U;
    } else if constexpr (std::is_same_v<T, uint16_t>) {
        return CV_16U;
    } else if constexpr (std::is_same_v<T, float>) {
        return CV_32F;
    } else if constexpr (std::is_same_v<T, double>) {
        return CV_64F;
    } else {
        static_assert(!std::is_same_v<T, T>, "Unsupported pixel value type");
    }
}

template <is_pixel_format F> constexpr int cv_type() {
    return CV_MAKETYPE(cv_depth<typename F::value_type>(), F::channels);
}

template <typename Src, typename Dst> consteval int cvt_color_code() {
    // --- Same format family (no color conversion needed) ---
    if constexpr (std::is_same_v<Src, Dst>) {
        return -1;
    }

    // Within gray family: Gray, Gray16, Float1
    if constexpr ((std::is_same_v<Src, Gray> || std::is_same_v<Src, Gray16> ||
                   std::is_same_v<Src, Float1>) &&
                  (std::is_same_v<Dst, Gray> || std::is_same_v<Dst, Gray16> ||
                   std::is_same_v<Dst, Float1>)) {
        return -1;
    }

    // Within BGR family: BGR, Float3
    if constexpr ((std::is_same_v<Src, BGR> || std::is_same_v<Src, Float3>) &&
                  (std::is_same_v<Dst, BGR> || std::is_same_v<Dst, Float3>)) {
        return -1;
    }

    // --- BGR/Float3 <-> RGB ---
    if constexpr ((std::is_same_v<Src, BGR> || std::is_same_v<Src, Float3>) &&
                  std::is_same_v<Dst, RGB>) {
        return cv::COLOR_BGR2RGB;
    }
    if constexpr (std::is_same_v<Src, RGB> &&
                  (std::is_same_v<Dst, BGR> || std::is_same_v<Dst, Float3>)) {
        return cv::COLOR_RGB2BGR;
    }

    // --- BGR/Float3 <-> Gray family ---
    if constexpr ((std::is_same_v<Src, BGR> || std::is_same_v<Src, Float3>) &&
                  (std::is_same_v<Dst, Gray> || std::is_same_v<Dst, Gray16> ||
                   std::is_same_v<Dst, Float1>)) {
        return cv::COLOR_BGR2GRAY;
    }
    if constexpr ((std::is_same_v<Src, Gray> || std::is_same_v<Src, Gray16> ||
                   std::is_same_v<Src, Float1>) &&
                  (std::is_same_v<Dst, BGR> || std::is_same_v<Dst, Float3>)) {
        return cv::COLOR_GRAY2BGR;
    }

    // --- RGB <-> Gray family ---
    if constexpr (std::is_same_v<Src, RGB> &&
                  (std::is_same_v<Dst, Gray> || std::is_same_v<Dst, Gray16> ||
                   std::is_same_v<Dst, Float1>)) {
        return cv::COLOR_RGB2GRAY;
    }
    if constexpr ((std::is_same_v<Src, Gray> || std::is_same_v<Src, Gray16> ||
                   std::is_same_v<Src, Float1>) &&
                  std::is_same_v<Dst, RGB>) {
        return cv::COLOR_GRAY2RGB;
    }

    // --- BGR/Float3 <-> BGRA ---
    if constexpr ((std::is_same_v<Src, BGR> || std::is_same_v<Src, Float3>) &&
                  std::is_same_v<Dst, BGRA>) {
        return cv::COLOR_BGR2BGRA;
    }
    if constexpr (std::is_same_v<Src, BGRA> &&
                  (std::is_same_v<Dst, BGR> || std::is_same_v<Dst, Float3>)) {
        return cv::COLOR_BGRA2BGR;
    }

    // --- RGB <-> RGBA ---
    if constexpr (std::is_same_v<Src, RGB> && std::is_same_v<Dst, RGBA>) {
        return cv::COLOR_RGB2RGBA;
    }
    if constexpr (std::is_same_v<Src, RGBA> && std::is_same_v<Dst, RGB>) {
        return cv::COLOR_RGBA2RGB;
    }

    // --- BGR/Float3 <-> RGBA ---
    if constexpr ((std::is_same_v<Src, BGR> || std::is_same_v<Src, Float3>) &&
                  std::is_same_v<Dst, RGBA>) {
        return cv::COLOR_BGR2RGBA;
    }
    if constexpr (std::is_same_v<Src, RGBA> &&
                  (std::is_same_v<Dst, BGR> || std::is_same_v<Dst, Float3>)) {
        return cv::COLOR_RGBA2BGR;
    }

    // --- RGB <-> BGRA ---
    if constexpr (std::is_same_v<Src, RGB> && std::is_same_v<Dst, BGRA>) {
        return cv::COLOR_RGB2BGRA;
    }
    if constexpr (std::is_same_v<Src, BGRA> && std::is_same_v<Dst, RGB>) {
        return cv::COLOR_BGRA2RGB;
    }

    // --- Gray family <-> BGRA ---
    if constexpr ((std::is_same_v<Src, Gray> || std::is_same_v<Src, Gray16> ||
                   std::is_same_v<Src, Float1>) &&
                  std::is_same_v<Dst, BGRA>) {
        return cv::COLOR_GRAY2BGRA;
    }
    if constexpr (std::is_same_v<Src, BGRA> &&
                  (std::is_same_v<Dst, Gray> || std::is_same_v<Dst, Gray16> ||
                   std::is_same_v<Dst, Float1>)) {
        return cv::COLOR_BGRA2GRAY;
    }

    // --- Gray family <-> RGBA ---
    if constexpr ((std::is_same_v<Src, Gray> || std::is_same_v<Src, Gray16> ||
                   std::is_same_v<Src, Float1>) &&
                  std::is_same_v<Dst, RGBA>) {
        return cv::COLOR_GRAY2RGBA;
    }
    if constexpr (std::is_same_v<Src, RGBA> &&
                  (std::is_same_v<Dst, Gray> || std::is_same_v<Dst, Gray16> ||
                   std::is_same_v<Dst, Float1>)) {
        return cv::COLOR_RGBA2GRAY;
    }

    // --- BGRA <-> RGBA ---
    if constexpr (std::is_same_v<Src, BGRA> && std::is_same_v<Dst, RGBA>) {
        return cv::COLOR_BGRA2RGBA;
    }
    if constexpr (std::is_same_v<Src, RGBA> && std::is_same_v<Dst, BGRA>) {
        return cv::COLOR_RGBA2BGRA;
    }

    return -2;
}

} // namespace detail

// ── Constructors ──────────────────────────────────────────────

template <is_pixel_format format_type>
Image<format_type>::Image(int width, int height)
    : mat_(height, width, detail::cv_type<format_type>()) {}

template <is_pixel_format format_type>
Image<format_type>::Image(int width, int height, value_type fill_value)
    : mat_(height, width, detail::cv_type<format_type>(),
           cv::Scalar::all(static_cast<double>(fill_value))) {}

template <is_pixel_format format_type> Image<format_type>::Image(cv::Mat image)
    : mat_(std::move(image)) {}

// ── Accessors ─────────────────────────────────────────────────

template <is_pixel_format format_type>
cv::Mat& Image<format_type>::mat() noexcept {
    return mat_;
}

template <is_pixel_format format_type>
const cv::Mat& Image<format_type>::mat() const noexcept {
    return mat_;
}

template <is_pixel_format format_type>
int Image<format_type>::width() const noexcept {
    return mat_.cols;
}

template <is_pixel_format format_type>
int Image<format_type>::height() const noexcept {
    return mat_.rows;
}

template <is_pixel_format format_type>
size_t Image<format_type>::size_bytes() const noexcept {
    return mat_.total() * mat_.elemSize();
}

template <is_pixel_format format_type>
size_t Image<format_type>::stride() const noexcept {
    return mat_.step;
}

template <is_pixel_format format_type>
bool Image<format_type>::empty() const noexcept {
    return mat_.empty();
}

template <is_pixel_format format_type> auto Image<format_type>::data() noexcept
    -> value_type* {
    return reinterpret_cast<value_type*>(mat_.data);
}

template <is_pixel_format format_type>
auto Image<format_type>::data() const noexcept -> const value_type* {
    return reinterpret_cast<const value_type*>(mat_.data);
}

template <is_pixel_format format_type>
auto Image<format_type>::at(int row, int col, int ch) -> value_type& {
    return reinterpret_cast<value_type*>(mat_.ptr(row, col))[ch];
}

// ── Views ─────────────────────────────────────────────────────

template <is_pixel_format format_type>
ImageView<format_type> Image<format_type>::view() const noexcept {
    return ImageView<format_type>(data(), width(), height(),
                                  mat_.step / sizeof(value_type));
}

template <is_pixel_format format_type>
ImageMutView<format_type> Image<format_type>::mutable_view() const noexcept {
    return ImageMutView<format_type>(reinterpret_cast<value_type*>(mat_.data),
                                     width(), height(),
                                     mat_.step / sizeof(value_type));
}

template <is_pixel_format format_type>
ImageView<const format_type> Image<format_type>::const_view() const noexcept {
    return ImageView<const format_type>(data(), width(), height(),
                                        mat_.step / sizeof(value_type));
}

// ── Clone ─────────────────────────────────────────────────────

template <is_pixel_format format_type>
Image<format_type> Image<format_type>::clone() const {
    return Image(mat_.clone());
}

// ── Convert ───────────────────────────────────────────────────

template <is_pixel_format format_type> template <is_pixel_format target_format>
expected<Image<target_format>, ConversionalError>
Image<format_type>::convert_to() const {
    if (empty()) {
        return unexpected(ConversionalError::InternalError);
    }

    constexpr int code = detail::cvt_color_code<format_type, target_format>();
    if constexpr (code == -2) {
        return unexpected(ConversionalError::InternalError);
    }

    cv::Mat intermediate;

    // Step 1: color conversion
    if constexpr (code == -1) {
        intermediate = mat_;
    } else {
        cv::cvtColor(mat_, intermediate, code);
    }

    // Step 2: type conversion
    using dst_vt = typename target_format::value_type;
    cv::Mat result;
    if constexpr (!std::is_same_v<typename format_type::value_type, dst_vt>) {
        double scale = 1.0;
        if constexpr (std::is_same_v<typename format_type::value_type,
                                     uint8_t> &&
                      std::is_same_v<dst_vt, float>) {
            scale = 1.0 / 255.0;
        } else if constexpr (std::is_same_v<typename format_type::value_type,
                                            float> &&
                             std::is_same_v<dst_vt, uint8_t>) {
            scale = 255.0;
        } else if constexpr (std::is_same_v<typename format_type::value_type,
                                            uint8_t> &&
                             std::is_same_v<dst_vt, uint16_t>) {
            scale = 257.0;
        } else if constexpr (std::is_same_v<typename format_type::value_type,
                                            uint16_t> &&
                             std::is_same_v<dst_vt, uint8_t>) {
            scale = 1.0 / 257.0;
        }

        intermediate.convertTo(
            result,
            CV_MAKETYPE(detail::cv_depth<dst_vt>(), target_format::channels),
            scale);
    } else {
        result = std::move(intermediate);
    }

    return Image<target_format>(std::move(result));
}

} // namespace cvw

Updated on 2026-05-17 at 13:22:38 +0000

Built with VitePress