如何在OpenCV(C++)中将彩色图像转换为16位灰度图?
OpenCV 4.x将彩色图像转为16位灰度图的正确方法
问题背景
在OpenCV 4.1.2 + C++17环境下,需要将彩色图像转换为16位灰度图以获得更细腻的灰度渐变,同时实现像素访问与保存。现有代码可完成8位灰度转换,但直接尝试16位转换时出现内存异常,保存的JPG文件损坏,且数据未正确映射到16位范围。
现有8位转换代码:
#include <iostream> #include <string> #include <opencv2/opencv.hpp> #include <opencv2/highgui/highgui_c.h> #include <opencv2/core/mat.hpp> #include <opencv2/imgcodecs.hpp> int main() { cv::Mat image = cv::imread("test.jpg", -1); cv::Mat imageGray; cv::cvtColor(image, imageGray, cv::COLOR_BGR2GRAY); std::cout << "First pixel: " + std::to_string(imageGray.at<uchar>(0, 0)) + "\n"; cv::imwrite("gray.jpg", imageGray); return 0; }
核心问题分析
- 16位类型未正确初始化:直接用
at<ushort>访问未指定类型的Mat,会因内存布局不匹配触发异常; - 像素值未缩放:8位图像的0-255范围需映射到16位的0-65535,否则16位数据仅低8位有效,失去细腻度;
- 保存格式不兼容:JPEG格式仅支持8位图像,保存16位数据会导致文件损坏。
解决方案
正确转换流程
- 将8位彩色图像转换为16位彩色图像,同步完成像素值范围缩放;
- 将16位彩色图像转换为16位灰度图像;
- 使用
ushort类型访问16位灰度像素; - 保存为支持16位的图像格式(如PNG、TIFF)。
完整代码示例
#include <iostream> #include <string> #include <opencv2/opencv.hpp> int main() { // 加载8位彩色图像 cv::Mat image = cv::imread("test.jpg", cv::IMREAD_COLOR); if (image.empty()) { std::cerr << "图像加载失败!" << std::endl; return -1; } cv::Mat image16BGR; // 将8位BGR转16位BGR,缩放因子257(65535/255),把0-255映射到0-65535 image.convertTo(image16BGR, CV_16UC3, 257.0); cv::Mat imageGray16; // 将16位BGR转换为16位灰度图(类型CV_16UC1) cv::cvtColor(image16BGR, imageGray16, cv::COLOR_BGR2GRAY); // 访问16位灰度像素,使用ushort类型匹配CV_16UC1 ushort firstPixel = imageGray16.at<ushort>(0, 0); std::cout << "第一个像素(16位):" << firstPixel << "\n"; // 保存为PNG格式(支持16位灰度),禁止用JPG if (!cv::imwrite("gray_16bit.png", imageGray16)) { std::cerr << "图像保存失败!" << std::endl; return -1; } return 0; }
关键说明
convertTo的作用:完成图像类型转换的同时,通过缩放因子将8位像素值线性映射到16位的全范围,保证灰度渐变的细腻度;cvtColor的输出类型:输入为16位彩色图时,COLOR_BGR2GRAY会自动输出CV_16UC1类型的灰度图,与C++的ushort类型完全匹配,避免内存异常;- 保存格式选择:必须使用支持16位图像的格式(如PNG、TIFF),JPEG仅支持8位,无法正确存储16位数据。
内容的提问来源于stack exchange,提问作者Runsva
相关产品推荐
相关产品推荐

