Pergunta

Como posso girar a tonalidade de uma imagem usando ImageAttributes do GDI + (e presumivelmente ColorMatrix)?

Note que quero rodar o matiz, não tingir a imagem.

EDIT :. Ao rodar o matiz, quer dizer que cada cor na imagem deve ser deslocado para uma cor diferente, em vez de fazer toda a imagem de uma máscara de uma cor

Por exemplo,

Original: http://www.codeguru.com/img/legacy /gdi/Tinter03.jpg

Rodado: http://www.codeguru.com/img/legacy /gdi/Tinter15.jpg ou http: //www.codeguru .com / img / legacy / gdi / Tinter17.jpg

Foi útil?

Solução 2

acabei portar QColorMatrix para C # e usando seu método RotateHue.

Outras dicas

Eu joguei isso juntos para esta questão (ZIP arquivo com c # projeto vinculado na parte inferior do postar). Ele não usa ImageAttributes ou ColorMatrix, mas ele gira o matiz como você descreveu:

//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;
}

Você viu este artigo em CodeProject?

A partir de um olhar reconhecidamente rápida na página parece que matemática 4D. Você pode adotar uma abordagem semelhante para contstructing matrizes como você faria para matemática 2D ou 3D.

Tome uma série de "pontos" de origem - neste caso, você vai precisar de 4 - e os correspondentes "pontos"-alvo e gerar uma matriz. Este pode então ser aplicado a qualquer "ponto".

Para fazer isso em 2D (de memória para que eu pudesse ter feito um bugio completa neste):

Pontos Fonte são (1, 0) e (0, 1). Os alvos são (0, -1) e (1,0). A matriz que você precisa é:

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

Quando a informação extra é para o "w" valor da coordenada.

estender este até {R, G, B, A, w} e você terá uma matriz. Tome 4 cores vermelho (1, 0, 0, 0, w), verde (0, 1, 0, 0, w), Azul (0, 0, 1, 0, w) e transparente (0, 0, 0, 1, w). Trabalho com o que cores eles são mapeados para o novo esquema e construir a sua matriz da seguinte forma:

(P 1 , G 1 , B 1 , A 1 , 0)
(R 2 , G 2 , B 2 , A 2 , 0)
(R 3 , G 3 , B 3 , A 3 , 0)
(R 4 , G 4 , B 4 , A 4 , 0)
(0, 0, 0, 0, 1)

NOTA: A ordem que você mulitplication (vetor * matriz ou matriz * vector) irá determinar se os pontos transformados ir verticalmente ou horizontalmente para esta matriz, como a multiplicação de matrizes é não-comutativa. Estou assumindo vetor * matriz.

Esta é uma questão de idade, mas soluções postadas são muito mais complicada do que a simples resposta que eu encontrei.

Simples:

  • nenhuma dependência externa
  • cálculo não complicado (sem descobrir um ângulo de rotação, sem a aplicação de algum fórmula cosinus)
  • realmente faz uma rotação de cores!

Reafirmando o problema: o que precisamos

?

Eu preparei ícones coloridos vermelho. Algumas áreas são transparentes, alguns são mais ou menos saturada, mas todos eles têm tonalidade vermelha. Eu acho que corresponde ao seu caso de uso muito bem. As imagens podem ter outras cores, eles vão apenas ser rodados.

Como representar o tom para aplicar? A resposta mais simples é:. Fornecer uma Color

Trabalhar para uma solução

ColorMatrix representam uma transformação linear.

Obviamente, quando cor é vermelha, a transformação deve ser de identidade. Quando a cor é verde, a transformação deve mapa vermelho para verde, verde para azul, azul para vermelho.

A ColorMatrix que faz isso é:

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

solução matemática

O truque "Aha" é reconhecer que a forma real da matriz é

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

onde R, G e B são simplesmente o componente da cor tintométrico!

Exemplo de código

Eu levei exemplo de código na https://code.msdn.microsoft.com / ColorMatrix-image-Filtros-f6ed20ae .

Eu ajustei-o e realmente usar isso em meu projeto:

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;
    }
}

Espero que isso ajude.

Limitação

  • Observe que a transformação pode saturar se você usar cores muito brilhantes. Para garantir a ausência total de saturação, tt é suficiente que R + G + B <= 1.
  • Você pode normalizar a transformação dividindo cr, cg, cb por cr + cg + cb mas tratar o caso quando tingindo cor é preta ou você vai dividir por zero.

O código a seguir constrói um ColorMatrix para aplicar uma mudança de tonalidade.

A visão i teve foi que a 60 ° mudança no espaço matiz:

  • Red -> verde
  • green -> azul
  • blue -> RED

é, na verdade, uma mudança ° 45 no espaço RGB:

enter descrição da imagem aqui

Então você pode transformar uma fração de um deslocamento de 120 ° em alguma fração de um ° turno 90.

O parâmetro HueShift deve ser um valor entre -1..1.

Por exemplo, a fim de aplicar uma mudança de 60 °:

  • Alterando vermelho para amarelo,
  • mudando amarelo para verde
  • mudando verde para ciano
  • mudando cyan para azul
  • mudando azul para magenta
  • mudando magenta ao vermelho

você chama:

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

Implementação :

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;

Nota : Qualquer código é liberado para o domínio público. Sem atribuição necessário.

Suponho que www.aforgenet.com poderia ajudar

Eu construir um método que implementar o código @IanBoid em Linguagem 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
            );
    }
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top