Question

Comment puis-je faire pivoter la teinte d'une image à l'aide de GDI+ ImageAttributes (et probablement ColorMatrix)?

Notez que je souhaite faire pivoter la teinte, pas teinter l'image.

MODIFIER:Par rotation de la teinte, je veux dire que chaque couleur de l'image doit être décalée vers une couleur différente, au lieu de faire de l'image entière une nuance d'une seule couleur.

Par exemple,

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

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

Était-ce utile?

La solution 2

J'ai fini le portage QColorMatrix à C # et en utilisant la méthode de RotateHue.

Autres conseils

J'ai jeté cela ensemble pour cette question (fichier ZIP avec le projet c # lié au bas de la poster). Il n'utilise pas ImageAttributes ou ColorMatrix, mais il tourne la teinte que vous avez décrit:

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

Avez-vous vu cet article sur CodeProject?

D'un regard certes rapide à la page, il ressemble en mathématiques 4D. Vous pouvez adopter une approche similaire à des matrices contstructing comme vous le feriez pour les mathématiques 2D ou 3D.

Prenez une série de sources « points » - dans ce cas, vous aurez besoin 4 - et les cibles correspondantes « points » et générer une matrice. Cela peut ensuite être appliquée à tout « point ».

Pour ce faire, en 2D (de la mémoire pour que je puisse avoir fait une gaffe complète dans ce):

points de source sont (1, 0) et (0, 1). Les cibles sont (0, -1) et (1,0). La matrice dont vous avez besoin est:

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

Lorsque les informations supplémentaires est la valeur « w » de la coordonnée.

Étendre ce jusqu'à {R, G, B, A, w} et vous aurez une matrice. Prendre 4 couleurs Rouge (1, 0, 0, 0, w), Vert (0, 1, 0, 0, w), Bleu (0, 0, 1, 0, w) et transparent (0, 0, 0, 1, w). Le travail sur les couleurs à leur carte dans le nouveau système et construire votre matrice comme suit:

  

(R 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)

Remarque: L'ordre que vous faites vous mulitplication (vecteur * matrice ou matrice vecteur *) détermineront si les points transformés vont verticalement ou horizontalement dans cette matrice, comme la multiplication de la matrice est non-commutative. Je suppose que la matrice vecteur *.

Ceci est une question ancienne, mais les solutions sont affichées beaucoup plus compliqué que la simple réponse que je trouve.

simple:

  • aucune dépendance externe
  • aucun calcul compliqué (pas calculer un angle de rotation, pas d'appliquer une formule de cosinus)
  • ne fait une rotation de couleurs!

Reformuler le problème: qu'est-ce que nous avons besoin

J'ai préparé des icônes teintés rouge. Certaines zones sont transparents, certains sont plus ou moins saturés, mais ils ont tous la teinte rouge. Je pense qu'il correspond à votre cas d'utilisation très bien. Les images peuvent avoir d'autres couleurs, ils vont tout simplement être mis en rotation.

Comment représenter la teinte à appliquer? La réponse la plus simple est:. Fournir un Color

Vers une solution

ColorMatrix représentent une transformation linéaire.

Il est évident que quand la couleur est rouge, la transformation devrait être l'identité. Quand la couleur est verte, la transformation devrait carte rouge au vert, vert au bleu, du bleu au rouge.

Un ColorMatrix qui fait cela est:

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

Solution mathématique

L'astuce « Aha » est de reconnaître que la forme réelle de la matrice est

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

où R, G et B sont simplement le composant de la couleur teintant!

Exemple de code

Je pris exemple de code sur https://code.msdn.microsoft.com / ColorMatrix-image-filtres f6ed20ae .

ajusté et utilise effectivement dans mon projet:

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

Hope this helps.

Limitation

  • Notez que la transformation peut saturer si vous utilisez des couleurs trop vives. Pour garantir l'absence de saturation, tt suffit que R + G + B <= 1.
  • Vous pouvez normaliser la transformation en divisant cr, cg, cb par cr + cg + cb mais gérer le cas où la couleur teintant est noir ou vous allez diviser par zéro.

Le code suivant construit un ColorMatrix pour appliquer un changement de teinte.

L'idée que j'ai eue était qu'à un décalage de 60° dans l'espace de teinte :

  • rouge --> vert
  • vert --> bleu
  • bleu --> rouge

est en fait un décalage de 45° dans l'espace RVB :

enter image description here

Vous pouvez ainsi transformer une fraction d’un décalage de 120° en une fraction d’un décalage de 90°.

Le HueShift le paramètre doit être une valeur comprise entre -1..1.

Par exemple, pour appliquer un décalage de 60° :

  • changer le rouge en jaune,
  • changer le jaune en vert
  • changer le vert en cyan
  • changer le cyan en bleu
  • changer le bleu en magenta
  • changer le magenta en rouge

tu appelles:

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

Mise en œuvre:

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;

Note:Tout code est publié dans le domaine public.Aucune attribution requise.

Je suppose que www.aforgenet.com pourrait aider

Je construis une méthode qui mettent en œuvre le code @IanBoid dans le langage 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
            );
    }
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top