代码之家  ›  专栏  ›  技术社区  ›  SLaks

使用C中的ImageAttributes旋转色调#

  •  2
  • SLaks  · 技术社区  · 15 年前

    如何使用gdi+旋转图像的色调 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

    7 回复  |  直到 8 年前
        1
  •  3
  •   Rex M    15 年前

    I threw this together 对于这个问题(在文章底部链接了c项目的zip文件)。它不使用 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;
    }
    
        2
  •  3
  •   SLaks    15 年前

    我最终放弃了 QColorMatrix 到C并使用其 RotateHue 方法。

        3
  •  2
  •   ChrisF    15 年前

    你见过吗? this article 关于代码项目?

    从一个公认的快速浏览页面,它看起来像4D数学。你可以采用与二维或三维数学相似的方法来构造矩阵。

    取一系列的源“点”——在本例中,您需要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)。找出它们在新方案中所对应的颜色,并按如下方式建立矩阵:

    (R) G ,B A ,0)
    (R) G ,B A ,0)
    (R) G ,B A ,0)
    (R) G ,B A ,0)
    (0,0,0,0,0,0,1)

    注: 当矩阵乘法是不可交换的时,多重复制的顺序(向量*矩阵或矩阵*向量)将决定转换的点是垂直还是水平地进入这个矩阵。我假设向量矩阵。

        4
  •  1
  •   Stéphane Gourichon    10 年前

    这是一个古老的问题,但发布的解决方案比我找到的简单答案要复杂得多。

    简单的:

    • 无外部依赖
    • 没有复杂的计算(没有计算出旋转角度,没有应用一些余弦公式)
    • 实际上是旋转颜色!

    重申问题:我们需要什么?

    我准备了一些红色的图标。有些区域是透明的,有些或多或少是饱和的,但它们都有红色。我认为它与您的用例非常匹配。图像可能有其他颜色,它们将被旋转。

    如何表示要应用的色调?最简单的答案是:供应 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
    

    数学解

    “aha”技巧是认识到矩阵的实际形式是

    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来规范化转换,但要处理着色颜色为黑色或将除以零的情况。
        5
  •  1
  •   Ian Boyd    8 年前

    以下代码构造 ColorMatrix 用于应用色调偏移。

    我的见解是,在色调空间的60度偏移时:

    • 红色---gt;绿色
    • 绿色-->蓝色
    • 蓝色-红色

    实际上是RGB空间中的45度偏移:

    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;
    

    注释 :任何代码都被释放到公共域中。无需归因。

        6
  •  0
  •   Bogdan_Ch    15 年前

    我想那是 www.aforgenet.com 可以帮助

        7
  •  0
  •   riofly    8 年前

    我构建了一个用C语言实现@ianboid代码的方法。

        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
                );
        }
    
    推荐文章