2016-05-09 4 views
0

У меня есть 2 объекта Mat, overlay и background.Как сгладить 2 разных слоя изображения?

Как я кладу overlay Mat на вершине моих background Мата таким образом, что только непрозрачные пиксели overlay Мата полностью затемняет background Mat?

Я пробовал addWeighted(), который сочетает в себе 2 Мат, но оба «слоя» все еще видны.

  • overlay Мат имеет прозрачный канал в то время как background Мат не делает.
  • Пиксель в overlay Мат полностью прозрачный или полностью неясный.
  • Оба мата имеют одинаковый размер.
+0

Проверить [это] (http://stackoverflow.com/a/32481105/5008845), вы должны получить идею – Miki

ответ

0

Я был в состоянии редактируемого ответ порта api55 для Java:

private void merge(Mat background, Mat overlay) { 
      List<Mat> backgroundChannels = new ArrayList<>(); 
      Core.split(background, backgroundChannels); 

     List<Mat> overlayChannels = new ArrayList<>(); 
     Core.split(overlay, overlayChannels); 

     // compute "alphaRate = 1 - overlayAlpha/255" 
     Mat overlayAlphaChannel = overlayChannels.get(3); 
     Mat alphaRate = new Mat(overlayAlphaChannel.size(), overlayAlphaChannel.type()); 
     Core.divide(overlayAlphaChannel, new Scalar(255), alphaRate); 
     Core.absdiff(alphaRate, new Scalar(1), alphaRate); 

     for (int i = 0; i < 3; i++) { 
      // compute "(1 - alphaRate) * overlay" 
      Mat overlayChannel = overlayChannels.get(i); 
      Mat temp = new Mat(alphaRate.size(), alphaRate.type()); 
      Core.absdiff(alphaRate, new Scalar(1), temp); 
      Core.multiply(temp, overlayChannel, overlayChannel); 
      temp.release(); 

      // compute "background * alphaRate" 
      Mat backgroundChannel = backgroundChannels.get(i); 
      Core.multiply(backgroundChannel, alphaRate, backgroundChannel); 

      // compute the merged channel 
      Core.add(backgroundChannel, overlayChannel, backgroundChannel); 
     } 

     alphaRate.release(); 
     Core.merge(backgroundChannels, background); 
    } 

это намного быстрее по сравнению с расчетом двойного вложенного цикла.

1

Функция addWeighted не будет работать, поскольку она будет использовать одинаковое значение альфа для всех пикселей. Для того, чтобы делать то, что вы говорите, только заменить непрозрачное значение в фоновом режиме, вы можете создать небольшую функцию, которая, как это:

cv::Mat blending(cv::Mat& overlay, cv::Mat& background){ 
    //must have same size for this to work 
    assert(overlay.cols == background.cols && overlay.rows == background.rows); 
    cv::Mat result = background.clone(); 
    for (int i = 0; i < result.rows; i++){ 
     for (int j = 0; j < result.cols; j++){ 
      cv::Vec4b pix = overlay.at<cv::Vec4b>(i,j); 
      if (pix[3] == 0){ 
       result.at<cv::Vec3b>(i,j) = cv::Vec3b(pix[0], pix[1], pix[2]); 
      } 
     } 
    } 
    return result; 
} 

Я не уверен, если прозрачно значение в OpenCV является 0 или 255, поэтому измените его соответственно .... Я думаю, что это 0 для непрозрачного и 255 для полностью прозрачного.

Если вы хотите использовать значение альфа-канала как скорость, чтобы смешаться, а затем изменить его немного к этому:

cv::Mat blending(cv::Mat& overlay, cv::Mat& background){ 
    //must have same size for this to work 
    assert(overlay.cols == background.cols && overlay.rows == background.rows); 
    cv::Mat result = background.clone(); 
    for (int i = 0; i < result.rows; i++){ 
     for (int j = 0; j < result.cols; j++){ 
      cv::Vec4b pix = overlay.at<cv::Vec4b>(i,j); 
      double alphaRate = 1.0 - pix[3]/255.0; 
      result.at<cv::Vec3b>(i,j) = (1.0 - alphaRate) * cv::Vec3b(pix[0], pix[1], pix[2]) + result.at<cv::Vec3b>(i,j) * alphaRate; 
     } 
    } 
    return result; 
} 

К сожалению для кода, находящегося в C++, а не в JAVA, но я подумайте, что вы можете получить представление. В принципе, это всего лишь петля в пикселях и изменение пикселей в копии фона на фоне наложения, если они не прозрачны.

* EDIT *

Я отвечу на ваш комментарий с этим редактированием, так как это может занять место. Проблема заключается в том, как работает матрица OpenCV. Для изображения с альфой данные организованы как массив, такой как BGRA BGRA .... BGRA, и основные операции, такие как добавление, умножение и т. Д., Работают в матрицах с одинаковыми размерами ..... вы всегда можете попытаться отделить матрицу с помощью split (это запишет матрицу так, чтобы она была медленной), затем измените альфа-канал на двойной (опять же переписать), а затем произведите умножение и добавление матриц. Он должен быть быстрее, так как OpenCV оптимизирует эти функции .... и вы можете сделать это в ГПУ ....

Что-то вроде этого:

cv::Mat blending(cv::Mat& overlay, cv::Mat& background){ 
    std::vector<cv::Mat> channels; 
    cv::split(overlay, channels); 
    channels[3].convertTo(channels[3], CV_64F, 1.0/255.0); 
    cv::Mat newOverlay, result; 
    cv::merge(channels, newOverlay); 
    result = newOverlay * channels[3] + ((1 - channels[3]) * background); 
    return result; 
} 

Не уверен, что если OpenCV позволяет CV_8U умножить CV_64F , или если это будет быстрее или нет ... но может быть.

Кроме того, те, у которых есть петли, не имеют проблем в потоках, поэтому их можно оптимизировать ... запуск этого режима освобождения значительно увеличит скорость, так как функция OpenCV .at делает несколько утверждений .... что в режим освобождения не выполняется. Не уверен, что это может быть изменение в JAVA, хотя ...

+0

Этот подход слишком медленный для меня, поскольку я работаю с 12-мегапиксельными изображениями или больше. Можете ли вы предоставить реализацию, используя функции матричного массива, такие как 'add()', 'subtract()', 'multiply()' и т. Д.? – happymeal

+0

@happymeal Я обновил свой ответ, надеюсь, это поможет вам – api55