Last active
October 19, 2023 14:48
-
-
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+)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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; | |
} | |
} | |
} | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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