OpenCV cv :: cvtColor пропускает альфа-канал. Как сохранить альфа-данные?

Я понял несколько вещей:

  • что 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. Теперь функция утверждений, тем не менее, по-прежнему имеет место проблема с недопустимым доступом к памяти, вызывающая сбой.

2

Решение

Ну, в основном 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);

Пожалуйста, обратите внимание:

  1. Я не тестировал этот код!
  2. Возможно, потребуется настроить диапазоны данных (возвращаясь к convertTo () вместо оператора присваивания!
  3. Моя последняя операция может не перезаписывать данные поверхности (вы можете вернуться к mixChannels, который, как известно, никогда не выделяет никаких данных)
1

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]