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 cvwUpdated on 2026-05-17 at 13:22:38 +0000