Вопрос

Как я могу изменить оттенок изображения с помощью GDI + 's ImageAttributes (и предположительно ColorMatrix)?

Обратите внимание, что я хочу изменить оттенок, а не подкрасить изображение.

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

Например,

Оригинал:http://www.codeguru.com/img/legacy/gdi/Tinter03.jpg

Повернутый: http://www.codeguru.com/img/legacy/gdi/Tinter15.jpg или http://www.codeguru.com/img/legacy/gdi/Tinter17.jpg

Это было полезно?

Решение 2

В итоге я портировал QColorMatrix (Цветовая матрица) на C # и используя его RotateHue способ.

Другие советы

Я собрал это воедино по этому вопросу (ZIP-файл с проектом c #, связанный в нижней части сообщения).Он не использует ImageAttributes или ColorMatrix, но это меняет оттенок , как вы описали:

//rotate hue for a pixel
private Color CalculateHueChange(Color oldColor, float hue)
{
    HLSRGB color = new HLSRGB(
        Convert.ToByte(oldColor.R),
        Convert.ToByte(oldColor.G),
        Convert.ToByte(oldColor.B));

    float startHue = color.Hue;
    color.Hue = startHue + hue;
    return color.Color;
}

Вы видели эта статья в CodeProject?

По общему признанию, при беглом взгляде на страницу это выглядит как математика в формате 4D.Вы можете применить аналогичный подход к построению матриц, как и для 2D- или 3D-математики.

Возьмите ряд исходных "точек" - в данном случае вам понадобится 4 - и соответствующие целевые "точки" и сгенерируйте матрицу.Затем это может быть применено к любой "точке".

Чтобы сделать это в 2D (по памяти, чтобы я мог сделать полный ревун в этом):

Исходными точками являются (1, 0) и (0, 1).Целевыми значениями являются (0, -1) и (1,0).Матрица, которая вам нужна, это:

(0, -1, 0)
(1,  0, 0)
(0,  0, 1)

Где дополнительная информация относится к значению координаты "w".

Расширьте это до {R, G, B, A, w}, и вы получите матрицу.Возьмите 4 цвета: красный (1, 0, 0, 0, w), зеленый (0, 1, 0, 0, w), синий (0, 0, 1, 0, w) и прозрачный (0, 0, 0, 1, w).Определите, каким цветам они соответствуют в новой схеме, и создайте свою матрицу следующим образом:

(R1, Г1, Б1, А1, 0)
(R2, Г2, Б2, А2, 0)
(R3, Г3, Б3, А3, 0)
(R4, Г4, Б4, А4, 0)
(0,   0,   0,   0,   1)

ПРИМЕЧАНИЕ: Порядок, в котором вы выполняете умножение (вектор * матрица или матрица * вектор), будет определять, переходят ли преобразованные точки вертикально или горизонтально в эту матрицу, поскольку матричное умножение является некоммутативным.Я предполагаю, что вектор * матрица.

Это старый вопрос, но опубликованные решения намного сложнее, чем простой ответ, который я нашел.

Простой:

  • отсутствие внешней зависимости
  • никаких сложных вычислений (без определения угла поворота, без применения какой-либо формулы косинуса)
  • на самом деле происходит чередование цветов!

Повторное изложение проблемы:что нам нужно ?

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

Как представить оттенок для нанесения ?Самый простой ответ таков:снабдить Color.

Работа над решением

ColorMatrix представляют собой линейное преобразование.

Очевидно, что когда Цвет красный, трансформация должна быть идентичной.Когда цвет зеленый, преобразование должно преобразовать красный в зеленый, зеленый в синий, синий в красный.

Цветовая матрица , которая делает это, является :

0 1 0 0 0
0 0 1 0 0
1 0 0 0 0
0 0 0 1 0
0 0 0 0 1

Математическое решение

Хитрость "Ага" заключается в том, чтобы распознать, что фактическая форма матрицы является

R G B 0 0
B R G 0 0
G B R 0 0
0 0 0 1 0
0 0 0 0 1

где R, G и B - это просто компонент оттеночного цвета!

Пример кода

Я взял пример кода на https://code.msdn.microsoft.com/ColorMatrix-Image-Filters-f6ed20ae .

Я скорректировал его и фактически использую это в своем проекте:

static class IconTinter
{
    internal static Bitmap TintedIcon(Image sourceImage, Color tintingColor)
    {
        // Following https://code.msdn.microsoft.com/ColorMatrix-Image-Filters-f6ed20ae
        Bitmap bmp32BppDest = new Bitmap(sourceImage.Width, sourceImage.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);

        float cr = tintingColor.R / 255.0f;
        float cg = tintingColor.G / 255.0f;
        float cb = tintingColor.B / 255.0f;

        // See [Rotate Hue using ImageAttributes in C#](http://stackoverflow.com/a/26573948/1429390)
        ColorMatrix colorMatrix = new ColorMatrix(
            new float[][]
                       {new float[] { cr,  cg,  cb,  0,  0}, 
                        new float[] { cb,  cr,  cg,  0,  0}, 
                        new float[] { cg,  cb,  cr,  0,  0}, 
                        new float[] {  0,   0,   0,  1,  0}, 
                        new float[] {  0,   0,   0,  0,  1}
                       }
                       );

        using (Graphics graphics = Graphics.FromImage(bmp32BppDest))
        {
            ImageAttributes bmpAttributes = new ImageAttributes();
            bmpAttributes.SetColorMatrix(colorMatrix);

            graphics.DrawImage(sourceImage,
                new Rectangle(0, 0, sourceImage.Width, sourceImage.Height),
                0, 0,
                sourceImage.Width, sourceImage.Height,
                GraphicsUnit.Pixel, bmpAttributes);

        }

        return bmp32BppDest;
    }
}

Надеюсь, это поможет.

Ограничение

  • Обратите внимание, что трансформация может получиться насыщенной, если вы используете слишком яркие цвета.Чтобы гарантировать отсутствие насыщения, tt достаточно, чтобы R + G+ B<=1.
  • Вы можете нормализовать преобразование, разделив cr, cg, cb на cr + cg + cb, но учитывайте случай, когда цвет тонировки черный, иначе вы будете делить на ноль.

Следующий код создает ColorMatrix для применения изменения оттенка.

Понимание, которое у меня было, заключалось в том, что при сдвиге на 60 ° в пространстве оттенков:

  • красный -> зеленый
  • зеленый -> синий
  • синий -> красный

на самом деле это сдвиг на 45 ° в пространстве RGB:

enter image description here

Таким образом, вы можете превратить некоторую долю сдвига на 120 ° в некоторую долю сдвига на 90 °.

В HueShift параметр должен быть значением между -1..1.

Например, для того, чтобы применить сдвиг на 60°:

  • меняем красный на желтый,
  • меняем желтый на зеленый
  • меняем зеленый на голубой
  • изменение голубого цвета на синий
  • меняем синий на пурпурный
  • меняем пурпурный цвет на красный

ты звонишь:

var cm = GetHueShiftColorMatrix(60/360); //a value between 0..1

Реализация:

function GetHueShiftColorMatrix(HueShift: Real): TColorMatrix;
var
    theta: Real;
    c, s: Real;
const
    wedge = 120/360;
begin
    if HueShift > 1 then
        HueShift := 0
    else if HueShift < -1 then
        HueShift := 0
    else if Hueshift < 0 then
        HueShift := 1-HueShift;

    if (HueShift >= 0) and (HueShift <= wedge) then
    begin
        //Red..Green
        theta := HueShift / wedge*(pi/2);
        c := cos(theta);
        s := sin(theta);

        cm[0, 0] :=  c; cm[0, 1] :=  0; cm[0, 2] :=  s; cm[0, 3] :=  0; cm[0, 4] := 0;
        cm[1, 0] :=  s; cm[1, 1] :=  c; cm[1, 2] :=  0; cm[1, 3] :=  0; cm[1, 4] := 0;
        cm[2, 0] :=  0; cm[2, 1] :=  s; cm[2, 2] :=  c; cm[2, 3] :=  0; cm[2, 4] := 0;
        cm[3, 0] :=  0; cm[3, 1] :=  0; cm[3, 2] :=  0; cm[3, 3] :=  1; cm[3, 4] := 0;
        cm[4, 0] :=  0; cm[4, 1] :=  0; cm[4, 2] :=  0; cm[4, 3] :=  0; cm[4, 4] := 1;
    end
    else if (HueShift >= wedge) and (HueShift <= (2*wedge)) then
    begin
        //Green..Blue
        theta := (HueShift-wedge) / wedge*(pi/2);
        c := cos(theta);
        s := sin(theta);

        cm[0, 0] :=  0; cm[0, 1] :=  s; cm[0, 2] :=  c; cm[0, 3] :=  0; cm[0, 4] := 0;
        cm[1, 0] :=  c; cm[1, 1] :=  0; cm[1, 2] :=  s; cm[1, 3] :=  0; cm[1, 4] := 0;
        cm[2, 0] :=  s; cm[2, 1] :=  c; cm[2, 2] :=  0; cm[2, 3] :=  0; cm[2, 4] := 0;
        cm[3, 0] :=  0; cm[3, 1] :=  0; cm[3, 2] :=  0; cm[3, 3] :=  1; cm[3, 4] := 0;
        cm[4, 0] :=  0; cm[4, 1] :=  0; cm[4, 2] :=  0; cm[4, 3] :=  0; cm[4, 4] := 1;
    end
    else
    begin
        //Blue..Red
        theta := (HueShift-2*wedge) / wedge*(pi/2);
        c := cos(theta);
        s := sin(theta);

        cm[0, 0] :=  s; cm[0, 1] :=  c; cm[0, 2] :=  0; cm[0, 3] :=  0; cm[0, 4] := 0;
        cm[1, 0] :=  0; cm[1, 1] :=  s; cm[1, 2] :=  c; cm[1, 3] :=  0; cm[1, 4] := 0;
        cm[2, 0] :=  c; cm[2, 1] :=  0; cm[2, 2] :=  s; cm[2, 3] :=  0; cm[2, 4] := 0;
        cm[3, 0] :=  0; cm[3, 1] :=  0; cm[3, 2] :=  0; cm[3, 3] :=  1; cm[3, 4] := 0;
        cm[4, 0] :=  0; cm[4, 1] :=  0; cm[4, 2] :=  0; cm[4, 3] :=  0; cm[4, 4] := 1;
    end;

    Result := cm;
end;

Примечание:Любой код становится общедоступным.Указание авторства не требуется.

Я полагаю , что www.aforgenet.com мог бы помочь

Я создаю метод, который реализует код @IanBoid на языке c #.

    public void setHueRotate(Bitmap bmpElement, float value) {

        const float wedge = 120f / 360;

        var hueDegree = -value % 1;
        if (hueDegree < 0) hueDegree += 1;

        var matrix = new float[5][];

        if (hueDegree <= wedge)
        {
            //Red..Green
            var theta = hueDegree / wedge * (Math.PI / 2);
            var c = (float)Math.Cos(theta);
            var s = (float)Math.Sin(theta);

            matrix[0] = new float[] { c, 0, s, 0, 0 };
            matrix[1] = new float[] { s, c, 0, 0, 0 };
            matrix[2] = new float[] { 0, s, c, 0, 0 };
            matrix[3] = new float[] { 0, 0, 0, 1, 0 };
            matrix[4] = new float[] { 0, 0, 0, 0, 1 };

        } else if (hueDegree <= wedge * 2)
        {
            //Green..Blue
            var theta = (hueDegree - wedge) / wedge * (Math.PI / 2);
            var c = (float) Math.Cos(theta);
            var s = (float) Math.Sin(theta);

            matrix[0] = new float[] {0, s, c, 0, 0};
            matrix[1] = new float[] {c, 0, s, 0, 0};
            matrix[2] = new float[] {s, c, 0, 0, 0};
            matrix[3] = new float[] {0, 0, 0, 1, 0};
            matrix[4] = new float[] {0, 0, 0, 0, 1};

        }
        else
        {
            //Blue..Red
            var theta = (hueDegree - 2 * wedge) / wedge * (Math.PI / 2);
            var c = (float)Math.Cos(theta);
            var s = (float)Math.Sin(theta);

            matrix[0] = new float[] {s, c, 0, 0, 0};
            matrix[1] = new float[] {0, s, c, 0, 0};
            matrix[2] = new float[] {c, 0, s, 0, 0};
            matrix[3] = new float[] {0, 0, 0, 1, 0};
            matrix[4] = new float[] {0, 0, 0, 0, 1};
        }

        Bitmap originalImage = bmpElement;

        var imageAttributes = new ImageAttributes();
        imageAttributes.ClearColorMatrix();
        imageAttributes.SetColorMatrix(new ColorMatrix(matrix), ColorMatrixFlag.Default, ColorAdjustType.Bitmap);

        grpElement.DrawImage(
            originalImage, elementArea,
            0, 0, originalImage.Width, originalImage.Height,
            GraphicsUnit.Pixel, imageAttributes
            );
    }
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top