如何使用 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

有帮助吗?

解决方案 2

我结束了移植 QColorMatrix 以C#和使用它的方法RotateHue

其他提示

我把此一起了解这个问题(与的底部连接的C#项目ZIP文件后)。它不使用ImageAttributesColorMatrix,但它也旋转你所描述的色调:

//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数学。您可以采用类似的方法来contstructing矩阵,你会为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)。制定出它们映射什么颜色在新的方案,并建立你的矩阵如下:

  

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

注意:的顺序做自己mulitplication(矢量*矩阵或矩阵*向量)将决定变换点是否垂直或水平进入这个矩阵,如矩阵乘法是不可交换的。我假定向量*矩阵。

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

简单的:

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

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

我准备了红色的图标。有些区域是透明的,有些区域或多或少饱和,但它们都具有红色调。我认为它非常适合您的用例。图像可能有其他颜色,它们只会被旋转。

如何表示要应用的色调?最简单的答案是:供应一个 Color.

努力寻求解决方案

ColorMatrix 表示线性变换。

显然,当颜色为红色时,变换应该是恒等的。当颜色为绿色时,转换应将红色映射到绿色,将绿色映射到蓝色,将蓝色映射到红色。

执行此操作的 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° 偏移时:

  • 红色-->绿色
  • 绿色-->蓝色
  • 蓝色-->红色

实际上是 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;

笔记:任何代码都会发布到公共领域。无需归属。

我假定 www.aforgenet.com 可以帮助

我建立实现在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
            );
    }
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top