如何旋转图片框图像中的色调?
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();
}
}
这里有两个问题:
我以为 "float degrees = 60f;" 会旋转 60 度,结果我旋转了大约 180 度。如何更正此问题以便获得正确的色调旋转量?我期望 60 度而得到 180 度是一个巨大的错误。
结果没有作为新图像添加到 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 =
定义以始终从单独的位置选择原始图像。
更新
使用 Graphics
、ImageAttributes
和 ColorMatrix
方法,按钮事件处理程序可以这样写:
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;
}
首先我应该指出,我只花了 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();
}
}
这里有两个问题:
我以为 "float degrees = 60f;" 会旋转 60 度,结果我旋转了大约 180 度。如何更正此问题以便获得正确的色调旋转量?我期望 60 度而得到 180 度是一个巨大的错误。
结果没有作为新图像添加到 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 =
定义以始终从单独的位置选择原始图像。
更新
使用 Graphics
、ImageAttributes
和 ColorMatrix
方法,按钮事件处理程序可以这样写:
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;
}