Как изменить определенный цвет в изображении?

Мой вопрос в том, что если у меня есть изображение льва, я просто хочу изменить цвет льва, а не цвет фона. Для этого я назвал этот вопрос SO, но он превращает цвет всего изображения. Кроме того, изображение не выглядит великолепным. Мне нужно изменение цвета, как Photoshop. возможно ли это сделать в coregraphics или я должен использовать любую другую библиотеку.

EDIT: Мне нужно изменить цвет как iQuikColor app

enter image description here

Ответ 1

См. ответы ниже. Шахта не дает полного решения.


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

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

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

Ответ 2

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

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

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

введите описание изображения здесь

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

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

введите описание изображения здесь

введите описание изображения здесь

Обратите внимание, что он не окрашивает шины или задние фонари, потому что он находится за пределами 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)
    }
}

Здесь можно скачать образец проекта. Он использует Xcode 7 и Swift 2.0.

Ответ 3

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

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

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

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

  • Оттенок - основной воспринимаемый цвет, т.е. красный, зеленый, оранжевый и т.д.
  • Насыщенность - как "полный" цвет - то есть от полного цвета до какого-либо цвета вообще
  • Легкость - насколько яркий цвет

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

400px-HueScale.svg.png

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

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

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

Как только вы узнаете, какие пиксели вы хотите изменить, чтобы изменить цвет.

Самый простой способ раскрасить пиксели - это просто изменить оттенок, оставив Насыщенность и Легкость от исходного пикселя. Например, если вы хотите создать зеленые пиксели magenta, вы добавите 180 градусов к всем значениям оттенка выбранных пикселей (убедитесь, что вы используете математику по модулю 360).

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

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

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

Ответ 4

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

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

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

Ответ 5

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

Предполагая, что у вас есть CGImage (или a 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 (это открытый редактор изображений) - у них есть algos для обнаружения краев и выбора по цвету.