Как получить реальные значения цвета RGBA или ARGB без предварительно умноженного альфа-канала? - PullRequest
5 голосов
/ 18 июня 2011

Я создаю растровый контекст, используя CGBitmapContextCreate с опцией kCGImageAlphaPremultipliedFirst.

Я сделал тестовое изображение 5 x 5 с некоторыми основными цветами (чисто красный, зеленый, синий, белый, черный), несколькими смешанными цветами (то есть фиолетовым) в сочетании с некоторыми альфа-вариациями. Каждый раз, когда альфа-компонент не равен 255, значение цвета неверно.

Я обнаружил, что могу пересчитать цвет, когда сделаю что-то вроде:

almostCorrectRed = wrongRed * (255 / alphaValue);
almostCorrectGreen = wrongGreen * (255 / alphaValue);
almostCorrectBlue = wrongBlue * (255 / alphaValue);

Но проблема в том, что мои расчеты иногда отключаются на 3 или даже больше. Так, например, я получаю значение 242 вместо 245 для зеленого, и я на 100% уверен, что оно должно быть точно 245. Альфа - 128.

Затем для точно такого же цвета только с разной непрозрачностью альфа в растровом изображении PNG я получаю альфа = 255 и зеленый = 245, как и должно быть.

Если альфа равна 0, тогда красный, зеленый и синий также равны 0. Здесь все данные потеряны, и я не могу определить цвет пикселя.

Как мне вообще избежать или отменить это предварительное умножение альфа-канала, чтобы я мог изменять пиксели в моем изображении на основе истинных значений пикселей R G B, какими они были при создании изображения в Photoshop? Как я могу восстановить исходные значения для R, G, B и A?


Справочная информация (вероятно, не требуется для этого вопроса):

Что я делаю, так это: я беру UIImage, рисую его в контексте растрового изображения, чтобы выполнить на нем несколько простых алгоритмов манипулирования изображениями, меняя цвет каждого пикселя в зависимости от того, какой цвет был раньше , Ничего особенного. Но мой код нуждается в реальных цветах. Когда пиксель прозрачен (то есть его альфа меньше 255), мой алгоритм не должен заботиться об этом, он должен просто изменять R, G, B по мере необходимости, в то время как Alpha остается на том же уровне. Иногда, хотя это будет сдвигать альфа вверх или вниз тоже Но я вижу их как две разные вещи. Альфа контролирует прозрачность, а R G B контролирует цвет.

Ответы [ 3 ]

6 голосов
/ 18 июня 2011

Это фундаментальная проблема с предварительным умножением в целочисленном виде:

  • 245 * (128/255) = 122,98
  • 122,98 усечено до целого числа= 122
  • 122 * (255/128) = 243.046875

Я не уверен, почему вы получаете 242 вместо 243, но эта проблема остается в любом случае, и этоЧем хуже становится альфа, тем хуже.

Решение состоит в том, чтобы использовать компоненты с плавающей запятой .Руководство по программированию в Quartz 2D дает полную информацию о формате, который вам нужно будет использовать .

Важный момент: вам нужно будет использовать плавающую точку при создании исходного изображения(и я не думаю, что даже возможно сохранить такое изображение как PNG; возможно, вам придется использовать TIFF).Изображение, которое уже было предварительно умножено на интегральный тип, уже потеряло эту точность;получить его обратно не получится.

Случай с нулевой альфа-версией является крайней версией этого, до такой степени, что даже с плавающей запятой не может помочь вам.Все, что умножено на ноль (альфа), равно нулю, и исходное неумноженное значение из этой точки не восстанавливается.

0 голосов
/ 21 марта 2013

Я столкнулся с той же проблемой при попытке прочитать данные изображения, отобразить его на другом изображении с помощью CoreGraphics, а затем сохранить результат как данные без предварительного умножения. Решение, которое я нашел и которое сработало для меня, состояло в том, чтобы сохранить таблицу, содержащую точное отображение, которое CoreGraphics использует для сопоставления данных без предварительного умножения с данными, предварительно умноженными. Затем оцените, какое будет первоначальное предварительно умноженное значение с помощью вызовов mult и floor (). Затем, если оценка и результат поиска в таблице не совпадают, просто проверьте значение под оценкой и значение над оценкой в ​​таблице для точного соответствия.

// Execute premultiply logic on RGBA components split into componenets.
// For example, a pixel RGB (128, 0, 0) with A = 128
// would return (255, 0, 0) with A = 128

static
inline
uint32_t premultiply_bgra_inline(uint32_t red, uint32_t green, uint32_t blue, uint32_t alpha)
{
  const uint8_t* const restrict alphaTable = &extern_alphaTablesPtr[alpha * PREMULT_TABLEMAX];
  uint32_t result = (alpha << 24) | (alphaTable[red] << 16) | (alphaTable[green] << 8) | alphaTable[blue];
  return result;
}

static inline
int unpremultiply(const uint32_t premultRGBComponent, const float alphaMult, const uint32_t alpha)
{
  float multVal = premultRGBComponent * alphaMult;
  float floorVal = floor(multVal);
  uint32_t unpremultRGBComponent = (uint32_t)floorVal;
  assert(unpremultRGBComponent >= 0);
  if (unpremultRGBComponent > 255) {
    unpremultRGBComponent = 255;
  }

  // Pass the unpremultiplied estimated value through the
  // premultiply table again to verify that the result
  // maps back to the same rgb component value that was
  // passed in. It is possible that the result of the
  // multiplication is smaller or larger than the
  // original value, so this will either add or remove
  // one int value to the result rgb component to account
  // for the error possibility.

  uint32_t premultPixel = premultiply_bgra_inline(unpremultRGBComponent, 0, 0, alpha);

  uint32_t premultActualRGBComponent = (premultPixel >> 16) & 0xFF;

  if (premultRGBComponent != premultActualRGBComponent) {
    if ((premultActualRGBComponent < premultRGBComponent) && (unpremultRGBComponent < 255)) {
      unpremultRGBComponent += 1;
    } else if ((premultActualRGBComponent > premultRGBComponent) && (unpremultRGBComponent > 0)) {
      unpremultRGBComponent -= 1;
    } else {
      // This should never happen
      assert(0);
    }
  }

  return unpremultRGBComponent;
}

Вы можете найти полную статическую таблицу значений по этой ссылке на github .

Обратите внимание, что этот подход не будет восстанавливать информацию, "потерянную", когда исходный неумноженный пиксель был предварительно умножен. Но он возвращает наименьший не умноженный пиксель, который станет предварительно умноженным пикселем, когда снова пройдет логику предварительного умножения. Это полезно, когда графическая подсистема принимает только предварительно умноженные пиксели (как CoreGraphics в OSX). Если графическая подсистема принимает только предварительно умноженные пиксели, то лучше хранить только предварительно умноженные пиксели, поскольку потребляется меньше места по сравнению с неумноженными пикселями.

0 голосов
/ 18 июня 2011

Предварительное умножение альфа на целочисленный цветовой тип - это операция с потерями информации. Данные уничтожаются в процессе квантования (округление до 8 бит).

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

...