我已经意识到了几件事:
OpenCV不会处理cvtColor(RGBAMat, HSVXMat, RGB2HSV, 4)
中的Alpha通道,即使它静默接受并忽略了4通道参数和4通道输出矩阵。
OpenCV不转换cvtColor
中的数据类型,尽管它很乐意接受来自输入的不同数据类型的输出matix而没有警告。
当从SDL_Surfaces创建Mats时,OpenCV的CV_AUTOSTEP
作为Mat(int h, int w, enum val DATATYPE, char* data, size_t step)
的最后一个参数无法正常工作。它会导致使用surface->w * sizeof(whatever datatype the surface is)
补救的段错误。
由于这些认识,我接受了@ypnos的答案,因为它显然是在正确的轨道上。请注意,此页面上的所有代码都不会按原样运行。
void surface_change_sat(SDL_Surface* src_surf, SDL_Surface* dst_surf, float sat_diff) {
using namespace cv;
SDL_LockSurface(src_surf);
Mat src_mat = Mat(src_surf->h, src_surf->w, CV_8UC4); // Make mat from surf
memcpy(src_mat.data, src_surf->pixels, src_surf->h * src_surf->w * sizeof(Uint32));
SDL_UnlockSurface(src_surf);
Mat src_rgbI = Mat(src_mat.rows, src_mat.cols, CV_8UC3); // split rgba into rgb and a
Mat aI_mat = Mat(src_mat.rows, src_mat.cols, CV_8UC1); // since hsv has no a
Mat to_ar[] {src_rgbI, aI_mat};
int from_to[] = { 0,0, 1,1, 2,2, 3,3 }; // r=0, ... a=3
mixChannels(&src_mat, 1, to_ar, 2, from_to, 4);
Mat rgbF_mat = Mat(src_mat.rows, src_mat.cols, CV_32FC3); // Make ints into floats
src_rgbI.convertTo(rgbF_mat, CV_32F);
typedef Vec<float, 3> flt_vec_t; // The type of pixel in hsv
Mat hsv_mat = Mat(src_mat.rows, src_mat.cols, CV_32FC3);
cvtColor(rgbF_mat, hsv_mat, CV_RGB2HSV, 3); // convert to HSV
flt_vec_t pix_vec;
for (MatIterator_<flt_vec_t> mat_it = hsv_mat.begin<flt_vec_t>();
mat_it != hsv_mat.end<flt_vec_t>();
mat_it++) {
pix_vec = *mat_it;
Matx<float, 3, 1> pix_matx = (Matx<float, 3, 1>)pix_vec;
CV_Assert(pix_matx.val[1] <= 1.0f && pix_matx.val[1] >= 0.0f);
pix_matx.val[1] += sat_diff;
if (pix_matx.val[1] > 1.0f) { pix_matx.val[1] = 1.0f; }
if (pix_matx.val[1] < 0.0f) { pix_matx.val[1] = 0.0f; }
}
cvtColor(hsv_mat, rgbF_mat, CV_HSV2RGB, 3); // convert back to RGB
Mat dst_mat = Mat(dst_surf->h, dst_surf->w, CV_8UC4);
Mat dst_rgbI = Mat(dst_mat.rows, dst_mat.cols, CV_8UC3);
rgbF_mat.convertTo(dst_rgbI, CV_8U); // float back to int
Mat from_ar[] {dst_rgbI, aI_mat};
int to_from[] = { 0,0, 1,1, 2,2, 0,3 }; // r=0, ... a=3
mixChannels(from_ar, 2, &dst_mat, 1, to_from, 4); // add alpha for RGBA
SDL_LockSurface(dst_surf);
memcpy(dst_surf->pixels, (void*)dst_mat.data, dst_surf->h * dst_surf->w * sizeof(Uint32));
SDL_UnlockSurface(dst_surf);
}
// -------------------旧问题------------------------ -
在我将CV_Assert
添加到以下函数之前,我发现了最神秘的内存错误,并意识到OpenCV正在默默地破坏cv::Mat hsv_mat
中的alpha通道。我很困惑如何解决这个问题:一个注释,其他几个相关问题的答案建议使用shell脚本或像imagemagick这样的cli工具。这些在这种特殊情况下没有帮助。我需要一个独立的函数,其依赖性仅限于opencv和标准库。
注意:根据评论中的建议,我明确拆分了Alpha通道,并从Uint8转换为Float32。然而,断言功能现在仍然存在无效内存访问导致段错误的问题。
答案 0 :(得分:1)
嗯,基本上cvtColor只适用于一组固定的矩阵类型,包括通道的格式和数量。因此,混合和重新混合频道是不可避免的。
我要做的下一件事是使用模板化数据类型。它们可以让您更好地理解矩阵中的内容,但有时OpenCV也会设法违反它(至少在旧版本中)。
我就是这样做的:
SDL_LockSurface(surf);
cv::Mat4b rgba(surf->h, surf->w, surf->pixels);
std::vector<cv::Mat1b> channels_rgba;
cv::split(rgba, channels_rgba);
// create matrix with first three channels only
std::vector<cv::Mat1b> channels_rgb(channels_rgba.begin(),
channels_rgba.begin()+3);
cv::Mat3b rgb;
cv::merge(channels_rgb, rgb);
// create floating point representation
cv::Mat3f rgbhsv;
rgbhsv = rgb; // implicit conversion
// convert to HSV
cv::cvtColor(rgbhsv, rgbhsv, CV_RGB2HSV);
for (cv::Mat3f::iterator mat_it = rgbhsv.begin();
mat_it != rgbhsv.end(); mat_it++) {
cv::Vec3f &pix_vec = *mat_it;
pix_vec[0] += hue_diff;
if (pix_vec[0] >= 360.0f || pix_vec[0] < 0.0f) {
pix_vec[0] = (180.0f / M_PI) * (std::asin(std::sin((pix_vec[0] * M_PI / 180.0f))));
}
}
// convert back to RGB
cv::cvtColor(rgbhsv, rgbhsv, CV_HSV2RGB);
// back to unsigned char channels
rgb = rgbhsv; // implicit conversion
cv::split(rgb, channels_rgb);
// replace first three channels only
for (size_t i = 0; i < channels_rgb.size(); ++i)
channels_rgba[i] = channels_rgb[i];
cv::merge(channels_rgba, rgba);
SDL_UnlockSurface(surf);
请注意: