Как изменить определенный цвет на изображении? - PullRequest
39 голосов
/ 08 ноября 2011

У меня вопрос: есть ли у меня изображение Льва, я просто хочу изменить цвет льва, а не цвет фона.Для этого я передал этот ТАК * вопрос , но он меняет цвет всего изображения.Кроме того, изображение не выглядит великолепно.Мне нужно изменить цвет, как фотошоп.возможно ли это сделать в coregraphics или мне нужно использовать любую другую библиотеку.

РЕДАКТИРОВАТЬ: мне нужно, чтобы изменение цвета было похоже на iQuikColor app

enter image description here

Ответы [ 5 ]

35 голосов
/ 17 сентября 2015

Потребовалось много времени, чтобы выяснить это, в основном потому, что я хотел запустить его в Swift, используя Core Image и CIColorCube.

@ Мигель объясняет, как вам нужно заменить«Диапазон угла оттенка» с другим «Диапазон угла оттенка».Вы можете прочитать его пост выше для подробностей о том, что такое Угловой диапазон оттенка.

Я сделал быстрое приложение, которое заменяет синий грузовик по умолчанию ниже на то, что вы выберете на ползунке оттенка.

enter image description here

Вы можете передвинуть ползунок, чтобы сообщить приложению, какого цвета оттенок вы хотите заменить синим.

Я жестко задаю диапазон оттенков 60 градусов, который, как правило, кажется, охватывает большую часть определенного цвета, но вы можете отредактировать его, если нужно.

enter image description here

enter image description here

Обратите внимание, что он не окрашивает шины или задние фонари, потому что этоза пределами 60-градусного диапазона стандартного синего оттенка грузовика, но он правильно обрабатывает затенение.

Сначала вам нужен код для преобразования RGB в HSV (значение оттенка):

func RGBtoHSV(r : Float, g : Float, b : Float) -> (h : Float, s : Float, v : Float) {
    var h : CGFloat = 0
    var s : CGFloat = 0
    var v : CGFloat = 0
    let col = UIColor(red: CGFloat(r), green: CGFloat(g), blue: CGFloat(b), alpha: 1.0)
    col.getHue(&h, saturation: &s, brightness: &v, alpha: nil)
    return (Float(h), Float(s), Float(v))
}

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

func HSVtoRGB(h : Float, s : Float, v : Float) -> (r : Float, g : Float, b : Float) {
    var r : Float = 0
    var g : Float = 0
    var b : Float = 0
    let C = s * v
    let HS = h * 6.0
    let X = C * (1.0 - fabsf(fmodf(HS, 2.0) - 1.0))
    if (HS >= 0 && HS < 1) {
        r = C
        g = X
        b = 0
    } else if (HS >= 1 && HS < 2) {
        r = X
        g = C
        b = 0
    } else if (HS >= 2 && HS < 3) {
        r = 0
        g = C
        b = X
    } else if (HS >= 3 && HS < 4) {
        r = 0
        g = X
        b = C
    } else if (HS >= 4 && HS < 5) {
        r = X
        g = 0
        b = C
    } else if (HS >= 5 && HS < 6) {
        r = C
        g = 0
        b = X
    }
    let m = v - C
    r += m
    g += m
    b += m
    return (r, g, b)
}

Теперь выпросто переберите полный цветовой куб RGBA и «отрегулируйте» любые цвета в диапазоне оттенков «по умолчанию синий» с теми из вашего нового желаемого оттенка.Затем используйте Core Image и фильтр CIColorCube, чтобы применить отрегулированный цветовой куб к изображению.

func render() {
    let centerHueAngle: Float = 214.0/360.0 //default color of truck body blue
    let destCenterHueAngle: Float = slider.value
    let minHueAngle: Float = (214.0 - 60.0/2.0) / 360 //60 degree range = +30 -30
    let maxHueAngle: Float = (214.0 + 60.0/2.0) / 360
    var hueAdjustment = centerHueAngle - destCenterHueAngle
    let size = 64
    var cubeData = [Float](count: size * size * size * 4, repeatedValue: 0)
    var rgb: [Float] = [0, 0, 0]
    var hsv: (h : Float, s : Float, v : Float)
    var newRGB: (r : Float, g : Float, b : Float)
    var offset = 0
    for var z = 0; z < size; z++ {
        rgb[2] = Float(z) / Float(size) // blue value
        for var y = 0; y < size; y++ {
            rgb[1] = Float(y) / Float(size) // green value
            for var x = 0; x < size; x++ {
                rgb[0] = Float(x) / Float(size) // red value
                hsv = RGBtoHSV(rgb[0], g: rgb[1], b: rgb[2])
                if hsv.h < minHueAngle || hsv.h > maxHueAngle {
                    newRGB.r = rgb[0]
                    newRGB.g = rgb[1]
                    newRGB.b = rgb[2]
                } else {
                    hsv.h = destCenterHueAngle == 1 ? 0 : hsv.h - hueAdjustment //force red if slider angle is 360
                    newRGB = HSVtoRGB(hsv.h, s:hsv.s, v:hsv.v)
                }
                cubeData[offset]   = newRGB.r
                cubeData[offset+1] = newRGB.g
                cubeData[offset+2] = newRGB.b
                cubeData[offset+3] = 1.0
                offset += 4
            }
        }
    }
    let data = NSData(bytes: cubeData, length: cubeData.count * sizeof(Float))
    let colorCube = CIFilter(name: "CIColorCube")!
    colorCube.setValue(size, forKey: "inputCubeDimension")
    colorCube.setValue(data, forKey: "inputCubeData")
    colorCube.setValue(ciImage, forKey: kCIInputImageKey)
    if let outImage = colorCube.outputImage {
        let context = CIContext(options: nil)
        let outputImageRef = context.createCGImage(outImage, fromRect: outImage.extent)
        imageView.image = UIImage(CGImage: outputImageRef)
    }
}

Образец проекта можно загрузить здесь .

21 голосов
/ 10 ноября 2011

Смотрите ответы ниже.У меня нет полного решения.


Вот эскиз возможного решения с использованием OpenCV:

  • Преобразование изображения из RGB в HSV с помощью cvCvtColor (мы хотим изменить только оттенок).
  • Изолировать цвет с помощью cvThreshold, указывающего определенный допуск (требуется диапазон цветов, а не один плоский цвет).
  • Отменитьобласти цвета ниже минимального размера с использованием библиотеки обнаружения BLOB-объектов, например cvBlobsLib .Это избавит от точек схожего цвета в сцене.
  • Маскаруйте цвет с помощью cvInRangeS и используйте полученную маску, чтобы применить новый оттенок.
  • cvMerge новое изображениес новым оттенком с изображением, состоящим из каналов насыщенности и яркости, которые вы сохранили на первом этапе.

В сети есть несколько портов OpenCV iOS, например: http://www.eosgarden.com/en/opensource/opencv-ios/overview/ У меня естьЯ сам не пробовал, но, похоже, это хорошее направление для исследований.

13 голосов
/ 15 ноября 2011

Я собираюсь сделать предположение, что вы знаете, как выполнять эти основные операции, поэтому они не будут включены в мое решение:

  • загрузить изображение
  • получить значение RGB данного пикселя загруженного изображения
  • установить значение RGB данного пикселя
  • отобразить загруженное изображение и / или сохранить его обратно на диск.

Прежде всего, давайте рассмотрим, как вы можете описать исходный и целевой цвета.Очевидно, что вы не можете указать их как точные значения RGB, так как у фотографии будут небольшие различия в цвете.Например, зеленые пиксели на картинке грузовика, которую вы разместили, не совсем одинакового зеленого цвета.Цветовая модель RGB не очень хороша для выражения основных цветовых характеристик, поэтому вы получите гораздо лучшие результаты, если конвертировать пиксели в HSL. Здесь - это функции C для преобразования RGB в HSL и обратно.

Цветовая модель HSL описывает три аспекта цвета:

  1. Оттенок - основной воспринимаемый цвет- т.е. красный, зеленый, оранжевый и т. д.
  2. Насыщенность - насколько «полный» цвет - т.е. от полного цвета до полного отсутствия цвета
  3. Легкость - насколько яркий цвет

Так, например, если вы хотите найти все зеленые пиксели на изображении, вы преобразуете каждый пиксель из RGB в HSL, а затем ищите значения H, соответствующие зеленому, с некоторым допуском длязеленые "цвета.Ниже приведена диаграмма оттенков из Википедии:

image

Так что в вашем случае вы будете смотреть на пиксели с оттенком 120 +/- некоторое количество.Чем больше диапазон, тем больше цветов будет выбрано.Если вы сделаете свой диапазон слишком широким, вы начнете видеть выделение желтого и голубого пикселей, поэтому вам придется найти правильный диапазон, и вы даже можете предложить пользователю элементов управления вашего приложения выбрать этот диапазон.

В дополнение к выбору по оттенку, вы можете разрешить использование диапазонов для насыщенности и яркости, чтобы при желании можно было установить дополнительные ограничения для пикселей, которые вы хотите выбрать для раскрашивания.

Наконец, вы можетеЯ хочу предложить пользователю возможность нарисовать «выбор лассо», чтобы определенные части изображения могли быть исключены из раскрашивания.Вот как вы можете сказать приложению, что вы хотите, чтобы тело зеленого грузовика, а не зеленое колесо.

Как только вы знаете, какие пиксели вы хотите изменить, пришло время изменить их цвет.

Самый простой способ раскрасить пиксели - просто изменить оттенок, оставив насыщенность и яркость от исходного пикселя.Так, например, если вы хотите сделать зеленые пиксели пурпурными, вы добавите 180 градусов ко всем значениям оттенка выбранных пикселей (убедившись, что вы используете modulo 360 math).

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

Как только у вас будет цветной пиксель HSL, вы просто конвертируете его обратно в RGB и записываете обратно в изображение.

Надеюсь, это поможет.Последний комментарий, который я должен сделать, заключается в том, что значения оттенков в коде обычно записываются в диапазоне 0–255, но многие приложения отображают их в виде цветового круга с диапазоном от 0 до 360 градусов.Имейте это в виду!

1 голос
/ 10 ноября 2011

Могу ли я предложить вам использовать OpenCV ?Это библиотека для работы с изображениями с открытым исходным кодом, а также порт для iOS.Есть множество постов в блоге о том, как его использовать и настроить.

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

Он был разработан некоторыми людьми из MIT, так что, как можно было ожидать, он неплохо справляется с такими задачами, как обнаружение краев и отслеживание объектов.Я помню, как читал блог о том, как отделить определенный цвет от картинки с помощью OpenCV - примеры показали довольно хороший результат.См. здесь для примера.Оттуда я не могу вообразить, что было бы огромной работой фактически изменить отделенный цвет на что-то еще.

0 голосов
/ 10 ноября 2011

Я не знаю об операции CoreGraphics для этого, и я не вижу подходящий фильтр CoreImage для этого.Если это правильно, то вот толчок в правильном направлении:

Предполагается, что у вас есть CGImage (или uiImage.CGImage):

  • Начните с создания нового CGBitmapContext
  • Рисование исходного изображения в контексте растрового изображения
  • Получение дескриптора пиксельных данных растрового изображения

Узнайте, как устроен буфер, чтобы вы могли правильно заполнить2D массив значений пикселей, которые имеют вид:

typedef struct t_pixel {
  uint8_t r, g, b, a;
} t_pixel;

Затем создайте цвет для определения местоположения:

const t_pixel ColorToLocate = { 0,0,0,255 }; // << black, opaque

И его значение замещения:

const t_pixel SubstitutionColor = { 255,255,255,255 }; // << white, opaque
  • Перебирать пиксельный буфер контекста растрового изображения, создавая t_pixel s.
  • Когда вы найдете пиксель, который соответствует ColorToLocate, замените исходные значения значениями в SubstitutionColor.

  • Создать новый CGImage из CGBitmapContext.

Это самая простая часть!Все, что нужно сделать, это взять CGImage, заменить точные цветовые совпадения и создать новый CGImage.

То, что вы хотите, является более сложным.Для этой задачи вам понадобится хороший алгоритм обнаружения краев.

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

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

...