Skip to content

Instantly share code, notes, and snippets.

@m516
Last active October 19, 2023 14:48
Show Gist options
  • Save m516/91471bbba21b081a8880bd662aa1db44 to your computer and use it in GitHub Desktop.
Save m516/91471bbba21b081a8880bd662aa1db44 to your computer and use it in GitHub Desktop.
C++ functions for visualizing normalized OpenCV Mats of most datatypes and channel numbers (C++ 14+, OpenCV 4+)
#pragma once
#include <opencv2/opencv.hpp>
#include <limits>
#include <cmath>
#include "normalize_mat.hpp"
std::string type2str(int type) {
std::string r;
uchar depth = type & CV_MAT_DEPTH_MASK;
uchar chans = 1 + (type >> CV_CN_SHIFT);
switch ( depth ) {
case CV_8U: r = "8U"; break;
case CV_8S: r = "8S"; break;
case CV_16U: r = "16U"; break;
case CV_16S: r = "16S"; break;
case CV_32S: r = "32S"; break;
case CV_32F: r = "32F"; break;
case CV_64F: r = "64F"; break;
default: r = "User"; break;
}
r += "C";
r += (chans+'0');
return r;
}
void print_pixel_value(const cv::Mat& img, int x, int y) {
int type = CV_MAT_DEPTH(img.type());
int channels = img.channels();
std::stringstream ss;
ss << "Pixel value at (" << x << ", " << y << "): ";
switch (type) {
case CV_8U:
ss << img.at<cv::Vec3b>(y, x);
break;
case CV_8S:
ss << img.at<cv::Vec3s>(y, x);
break;
case CV_16U:
ss << img.at<cv::Vec3w>(y, x);
break;
case CV_16S:
ss << img.at<cv::Vec3s>(y, x);
break;
case CV_32F:
ss << img.at<cv::Vec3f>(y, x);
break;
case CV_64F:
ss << img.at<cv::Vec3d>(y, x);
break;
default:
ss << "Unsupported data type";
break;
}
std::cout << ss.str() << std::endl;
}
void onMouse(int event, int x, int y, int flags, void* userdata) {
if (event == cv::EVENT_MOUSEMOVE) {
cv::Mat* img = reinterpret_cast<cv::Mat*>(userdata);
if (x < img->cols && y < img->rows) {
print_pixel_value(*img, x, y);
}
}
}
void interactive_window(std::string window_name, cv::Mat &img) {
int type = CV_MAT_DEPTH(img.type());
if (type != CV_8U && type != CV_8S && type != CV_16U && type != CV_16S && type != CV_32F && type != CV_64F) {
std::cerr << "Error: Unsupported data type for interactive_window" << std::endl;
return;
}
cv::Mat normalized_img = img.clone();
normalizeMat(normalized_img);
cv::namedWindow(window_name, cv::WINDOW_AUTOSIZE);
cv::setMouseCallback(window_name, onMouse, &img);
while (true) {
cv::imshow(window_name, normalized_img);
if (cv::waitKey(10) >= 0) {
break;
}
}
}
#pragma once
#include <limits>
#include <algorithm>
#include <iostream>
#include <opencv2/opencv.hpp>
template <typename T>
T white_value();
template <>
uchar white_value<uchar>() { return 255; }
template <>
char white_value<char>() { return 127; }
template <>
ushort white_value<ushort>() { return 65535; }
template <>
short white_value<short>() { return 32767; }
template <>
int white_value<int>() { return std::numeric_limits<int>::max(); }
template <>
float white_value<float>() { return 1.0f; }
template <>
double white_value<double>() { return 1.0; }
template <typename T>
void normalizeMatChannel(cv::Mat& channel) {
T min_val = std::numeric_limits<T>::max();
T max_val = std::numeric_limits<T>::lowest();
T max_white = white_value<T>();
// Find minimum and maximum values considering only finite numbers
for (int y = 0; y < channel.rows; ++y) {
for (int x = 0; x < channel.cols; ++x) {
T val = channel.at<T>(y, x);
if (std::isfinite(val)) {
min_val = std::min(min_val, val);
max_val = std::max(max_val, val);
}
}
}
// Special handling for constant images
if (max_val == min_val) {
channel.setTo(max_white);
return;
}
T range = max_val - min_val;
// Perform normalization
for (int y = 0; y < channel.rows; ++y) {
for (int x = 0; x < channel.cols; ++x) {
T& val = channel.at<T>(y, x);
if (std::isfinite(val)) {
if constexpr (std::is_floating_point<T>::value) {
val = ((val - min_val) / range) * max_white;
} else {
val = static_cast<T>(((static_cast<long double>(val) - min_val) / range) * max_white);
}
}
}
}
}
void normalizeMat(cv::Mat& img) {
std::vector<cv::Mat> channels;
cv::split(img, channels);
switch (img.depth()) {
case CV_8U:
for (auto& ch : channels) normalizeMatChannel<uchar>(ch);
break;
case CV_8S:
for (auto& ch : channels) normalizeMatChannel<char>(ch);
break;
case CV_16U:
for (auto& ch : channels) normalizeMatChannel<ushort>(ch);
break;
case CV_16S:
for (auto& ch : channels) normalizeMatChannel<short>(ch);
break;
case CV_32S:
for (auto& ch : channels) normalizeMatChannel<int>(ch);
break;
case CV_32F:
for (auto& ch : channels) normalizeMatChannel<float>(ch);
break;
case CV_64F:
for (auto& ch : channels) normalizeMatChannel<double>(ch);
break;
default:
std::cerr << "Unsupported data type." << std::endl;
return;
}
cv::merge(channels, img);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment