Я понял несколько вещей:
что OpenCV не обрабатывает альфа-каналы в cvtColor(RGBAMat, HSVXMat, RGB2HSV, 4)
даже если он молча принимает и игнорирует 4-канальный параметр и 4-канальную выходную матрицу.
что OpenCV не конвертирует типы данных в cvtColor
хотя он с радостью принимает выходной matix другого типа данных из входных данных без предупреждения.
что OpenCV CV_AUTOSTEP
в качестве последнего параметра Mat(int h, int w, enum val DATATYPE, char* data, size_t step)
делает не работа при создании циновок из SDL_Surfaces. Это вызывает ошибки, которые исправляются с помощью 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
, Я запутался в том, как решить эту проблему: одна заметка, несколько других ответов на связанные вопросы предлагали что-то вроде использования сценариев оболочки или инструментов cli, таких как imagemagick. Это не полезно в данном конкретном случае. Мне нужна отдельная функция с зависимостями, ограниченными opencv и стандартной библиотекой.
ЗаметкаСледуя предложению в комментариях, я явно разделил альфа-канал и преобразовал из Uint8 в Float32. Теперь функция утверждений, тем не менее, по-прежнему имеет место проблема с недопустимым доступом к памяти, вызывающая сбой.
Ну, в основном 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);
Пожалуйста, обратите внимание:
Других решений пока нет …