如何旋转图片框图像中的色调?

How do I rotate hue in a picturebox image?

首先我应该指出,我只花了 4 个小时探索这个问题的各种变体。大多数答案都参考了 MSDN 上的教程,这些教程涉及获取一块颜色并绘制一个具有不同色调的新矩形。这与我想要做的非常不同。我想在图片框中拍摄图像并旋转整个图像的色调(从 1 度到 359 度)。

这个问题完美地展示了我想做的事情:Rotate Hue in C#。不幸的是,答案参考了 C/C++,而不是 C#。这是第一次,我没有 post 的代码,因为没有什么可以接近完成我想要完成的事情。谢谢你。

编辑:我只是在处理一些我认为不成功的代码,结果我的结果只是被隐藏了,正在进行一些色调更改,而且速度很快,只是不正确。这是我到目前为止所拥有的:

    private void ColorRotation_Click(object sender, EventArgs e)
    {

        if (pictureBoxMain.Image != null)
        {
            Bitmap image = new Bitmap(pictureBoxMain.Image);
            ImageAttributes imageAttributes = new ImageAttributes();
            int width = image.Width;
            int height = image.Height;
            float degrees = 60f;
            double r = degrees * System.Math.PI / 180; // degrees to radians 

            float[][] colorMatrixElements = { 
            new float[] {(float)System.Math.Cos(r),  (float)System.Math.Sin(r),  0,  0, 0},
            new float[] {(float)-System.Math.Sin(r),  (float)-System.Math.Cos(r),  0,  0, 0},
            new float[] {0,  0,  2,  0, 0},
            new float[] {0,  0,  0,  1, 0},
            new float[] {0, 0, 0, 0, 1}};

            ColorMatrix colorMatrix = new ColorMatrix(colorMatrixElements);

            imageAttributes.SetColorMatrix(
               colorMatrix,
               ColorMatrixFlag.Default,
               ColorAdjustType.Bitmap);

            Graphics myGraphics = CreateGraphics();

            Rectangle myRectangle = new Rectangle();
            pictureBoxMain.Image = image; 
            myRectangle.Width = pictureBoxMain.Width; 
            myRectangle.Height = pictureBoxMain.Height; 
            myGraphics.DrawImage(pictureBoxMain.Image, myRectangle, 30, 50, myRectangle.Width, myRectangle.Height, GraphicsUnit.Pixel, imageAttributes); 
            pictureBoxMain.Refresh(); 
            }        
    }

这里有两个问题:

  1. 我以为 "float degrees = 60f;" 会旋转 60 度,结果我旋转了大约 180 度。如何更正此问题以便获得正确的色调旋转量?我期望 60 度而得到 180 度是一个巨大的错误。

  2. 结果没有作为新图像添加到 pictureBox。它在表单上被绘制为一个矩形。我找不到 "myGraphics.DrawImage()" 的重载(我尝试了所有 30 个),它接受 pictureBox 以及所需的 "GraphicsUnit.Pixel" 和 "imageAttributes"。我如何更新此代码,以便对图片框中的图像进行更改而不是在表单上绘制?也许 myGraphics.DrawImage() 不是答案。

非常感谢

基于here提供的RGB转换方案,此代码将对HueRotatePictureBox控件中的图像执行HueRotateAngleSelector.Value度的色调旋转:

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Windows.Forms;
...

private void HueRotateButton_Click(object sender, EventArgs e)
{
    // Get the cosine and sine of the selected hue rotation angle
    var radians = Math.PI * (double)HueRotateAngleSelector.Value / 180.0;
    var cos = Math.Cos(radians);
    var sin = Math.Sin(radians);

    // Calculate the elements of the RGB transformation matrix
    var a00 = 0.213 + cos * 0.787 - sin * 0.213;
    var a01 = 0.213 - cos * 0.213 + sin * 0.143;
    var a02 = 0.213 - cos * 0.213 - sin * 0.787;
    var a10 = 0.715 - cos * 0.715 - sin * 0.715;
    var a11 = 0.715 + cos * 0.285 + sin * 0.140;
    var a12 = 0.715 - cos * 0.715 + sin * 0.715;
    var a20 = 0.072 - cos * 0.072 + sin * 0.928;
    var a21 = 0.072 - cos * 0.072 - sin * 0.283;
    var a22 = 0.072 + cos * 0.928 + sin * 0.072;

    // Get the current image from the picture box control, ...
    var bitmap = (Bitmap)HueRotatePictureBox.Image;
    var width = bitmap.Width;
    var height = bitmap.Height;

    // ... and open it for modification
    var bitmapData = bitmap.LockBits(
        new Rectangle(0, 0, width, height),
        ImageLockMode.ReadWrite,
        PixelFormat.Format32bppArgb);

    var scan0 = bitmapData.Scan0;
    var stride = bitmapData.Stride;

    // Copy the image pixels to a local byte array
    var length = height * stride;
    var bytes = new byte[length];
    Marshal.Copy(scan0, bytes, 0, length);

    // Loop over all pixels in the image
    for (var y = 0; y < height; y++)
    {
        var offset = stride * y;
        for (var x = 0; x < width; x++, offset += 4)
        {
            // Get the original RGB components for the individual pixel
            // (the alpha component should not be changed and is therefore ignored)
            double b = bytes[offset];
            double g = bytes[offset + 1];
            double r = bytes[offset + 2];

            // Apply the hue rotation transform
            var rr = Math.Max(0.0, Math.Min(255.0, r * a00 + g * a10 + b * a20));
            var gr = Math.Max(0.0, Math.Min(255.0, r * a01 + g * a11 + b * a21));
            var br = Math.Max(0.0, Math.Min(255.0, r * a02 + g * a12 + b * a22));

            // Update the RGB components
            bytes[offset] = (byte)br;
            bytes[offset + 1] = (byte)gr;
            bytes[offset + 2] = (byte)rr;
        }
    }

    // Bitmap editing is finished, transfer the updated byte array to the image pixels 
    // and "lock" the image again
    Marshal.Copy(bytes, 0, scan0, length);
    bitmap.UnlockBits(bitmapData);

    // Update the image in the picture box
    HueRotatePictureBox.Image = bitmap;
}

上面的代码将拍摄这样的图像:

180°的色调旋转会变成这样:

注意! 上面的代码始终采用图片框控件中的 当前 图像并应用色调旋转。因此,如果您应用 180° 色调旋转 两次 ,您将 return 应用于原始图像。如果您希望始终将色调旋转应用于 原始 图像,则应更新 var bitmap = 定义以始终从单独的位置选择原始图像。

更新
使用 GraphicsImageAttributesColorMatrix 方法,按钮事件处理程序可以这样写:

private void HueRotateButton_Click(object sender, EventArgs e)
{
    // Get the cosine and sine of the selected hue rotation angle
    var radians = Math.PI * (double)HueRotateAngleSelector.Value / 180.0;
    var cos = (float)Math.Cos(radians);
    var sin = (float)Math.Sin(radians);

    // Create an image attributes object from a hue rotation color matrix
    var colorMatrix =
        new ColorMatrix(
            new[]
                {
                    new[] { 0.213f + cos * 0.787f - sin * 0.213f, 0.213f - cos * 0.213f + sin * 0.143f, 0.213f - cos * 0.213f - sin * 0.787f, 0f, 0f }, 
                    new[] { 0.715f - cos * 0.715f - sin * 0.715f, 0.715f + cos * 0.285f + sin * 0.140f, 0.715f - cos * 0.715f + sin * 0.715f, 0f, 0f },
                    new[] { 0.072f - cos * 0.072f + sin * 0.928f, 0.072f - cos * 0.072f - sin * 0.283f, 0.072f + cos * 0.928f + sin * 0.072f, 0f, 0f }, 
                    new[] { 0f, 0f, 0f, 1f, 0f }, 
                    new[] { 0f, 0f, 0f, 0f, 1f }
                });
    var imageAttributes = new ImageAttributes();
    imageAttributes.SetColorMatrix(colorMatrix);

    // Get the current image from the picture box control
    var bitmap = (Bitmap)HueRotatePictureBox.Image;
    var width = bitmap.Width;
    var height = bitmap.Height;

    // Get a graphics object of the bitmap and draw the hue rotation
    // transformed image on the bitmap area
    var graphics = Graphics.FromImage(bitmap);
    graphics.DrawImage(
        bitmap,
        new Rectangle(0, 0, width, height),
        0,
        0,
        width,
        height,
        GraphicsUnit.Pixel,
        imageAttributes);

    // Update the image in the picutre box
    HueRotatePictureBox.Image = bitmap;
}