I should note first that I just spent 4 hours exploring various variations on this question. Most of the answers references tutorials on MSDN which dealt with taking a block of color and drawing a new rectangle with a different hue. This is very different than what I'm looking to do. I want to take an image in a picturebox and rotate the hue for the entire image (anywhere from 1 to 359 degrees).
This question shows perfectly what I want to do: Rotate Hue in C# . Unfortunately, the answers reference C/C++, not C#. This is a first, I have no code to post, since nothing came close to accomplishing what I'm trying to accomplish. Thank you.
EDIT: I was just working with some code that I thought was unsuccessfully, turns out my results were just hidden, some hue changes were being made, and rather quickly, just not correctly. Here's what I have thus far:
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();
}
}
There are two problems here:
I thought "float degrees = 60f;" would give me 60 degrees of rotation, it turns out I'm getting about 180 degrees of rotation. How do I correct this so I can get the correct amount of hue rotation? My expecting 60 degrees and getting 180 degrees is a huge error.
The result is not getting added to the pictureBox as a new image. It's getting painted on the form as a rectangle. I cannot find an overload (I tried all 30) for "myGraphics.DrawImage()" that accepts a pictureBox along with the required "GraphicsUnit.Pixel" and "imageAttributes". How can I update this code so that the changes will be made to the image in the picture box instead of drawn on the form? Perhaps myGraphics.DrawImage() is not the answer.
Many Thanks
Based on the RGB conversion scheme provided here , this code will perform a hue rotation of HueRotateAngleSelector.Value
degrees on the image in the HueRotatePictureBox
control:
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;
}
The above code will take an image like this:
And a 180° hue rotation will turn it into this:
NOTE! The above code always takes the current image in the picture box control and apply hue rotation. Therefore, if you apply 180° hue rotation twice you will return to the original image. If you prefer to always apply the hue rotation to the original image, the var bitmap =
definition should be updated to always pick the original image from a separate location.
UPDATE
Using the Graphics
, ImageAttributes
and ColorMatrix
approach instead, the button event handler could be written like this:
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;
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.