Ruota la tonalità utilizzando ImageAttributes in C#
-
22-08-2019 - |
Domanda
Come posso ruotare la tonalità di un'immagine utilizzando GDI+ ImageAttributes
(e presumibilmente ColorMatrix
)?
Tieni presente che desidero ruotare la tonalità, non colorare l'immagine.
MODIFICARE:Ruotando la tonalità, intendo che ogni colore dell'immagine dovrebbe essere spostato su un colore diverso, invece di rendere l'intera immagine una sfumatura di un colore.
Per esempio,
Originale:http://www.codeguru.com/img/legacy/gdi/Tinter03.jpg
Ruotato: http://www.codeguru.com/img/legacy/gdi/Tinter15.jpg O http://www.codeguru.com/img/legacy/gdi/Tinter17.jpg
Soluzione 2
Ho finito il porting QColorMatrix per C # e utilizzando il suo metodo RotateHue
.
Altri suggerimenti
ho buttato questo insieme per questa domanda (file ZIP con il progetto C # collegato alla parte inferiore della inviare). Non usa ImageAttributes
o ColorMatrix
, ma ruota la tonalità come hai descritto:
//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;
}
Hai visto questo articolo su CodeProject?
Da uno sguardo certamente veloce alla pagina sembra che la matematica 4D. È possibile adottare un approccio simile a contstructing matrici come si farebbe per la matematica 2D o 3D.
Prendere una serie di sorgenti "punti" - in questo caso avrete bisogno di 4 - e le corrispondenti "punti" di destinazione e generare una matrice. Questo può essere applicato a qualsiasi "punto".
Per fare questo in 2D (a memoria così ho potuto fare uno svarione completa in questo):
i punti di origine sono (1, 0) e (0, 1). Gli obiettivi sono (0, -1) e (1,0). La matrice di cui hai bisogno è:
(0, -1, 0)
(1, 0, 0)
(0, 0, 1)
Se le informazioni in più è per il valore "w" del punto.
Estendere questo fino a {R, G, B, A, w} e avrai una matrice. Prendere 4 colori Rosso (1, 0, 0, 0, w), Verde (0, 1, 0, 0, w), Blu (0, 0, 1, 0, w) e trasparente (0, 0, 0, 1, w). Capire che cosa i colori che mappano nel nuovo schema e costruire la vostra matrice come segue:
(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)
NOTA: L'ordine ti mulitplication (vettore * matrice o matrice * vettoriale) determinerà se i punti trasformati vanno verticalmente o orizzontalmente in questa matrice, come matrice moltiplicazione è non-commutativo. Sto assumendo matrice vettore *.
Questa è una vecchia questione, ma le soluzioni postate sono molto più complicata di quanto la semplice risposta che ho trovato.
Semplice:
- nessuna dipendenza esterna
- nessun calcolo complicato (senza capire un angolo di rotazione, senza applicazione di una formula cosinus)
- realmente fa una rotazione di colori!
Riaffermando il problema: cosa abbiamo bisogno
Ho preparato icone colorate di rosso. Alcune aree sono trasparenti, alcuni sono più o meno saturo, ma tutti hanno colore rosso. Penso che corrisponde al tuo caso d'uso molto bene. Le immagini possono avere altri colori, saranno solo essere ruotati.
Come rappresentare la tinta da applicare? La risposta più semplice è:. Fornire un Color
Lavorare per una soluzione
ColorMatrix
rappresentano una trasformazione lineare.
Ovviamente quando Il colore è rosso, la trasformazione deve essere l'identità. Quando il colore è verde, la trasformazione dovrebbe mappa rosso a verde, verde al blu, blu al rosso.
Un ColorMatrix che fa questo è:
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
soluzione matematica
Il trucco "aha" è riconoscere che la forma effettiva della matrice è
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
dove R, G e B sono semplicemente il componente del colore di tinta!
Codice di esempio
Ho preso esempio di codice su https://code.msdn.microsoft.com / ColorMatrix-image-Filtri-f6ed20ae .
Ho modificato e in realtà uso questo nel mio progetto:
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;
}
}
Spero che questo aiuti.
Limitazione
- Si noti che la trasformazione può saturare se si utilizzano i colori troppo brillanti. Per garantire, saturazione, tt è sufficiente che R + G + B <= 1.
- È possibile normalizzare la trasformazione dividendo cr, CG, CB da CR + cg + cb ma gestire il caso in cui il colore colorazione è nero o ti dividere per zero.
Il codice seguente costruisce a ColorMatrix
per applicare uno spostamento di tonalità.
L'intuizione che ho avuto è stata che con uno spostamento di 60° nello spazio della tonalità:
- rosso --> verde
- verde --> blu
- blu --> rosso
è in realtà uno spostamento di 45° nello spazio RGB:
Quindi puoi trasformare una frazione di uno spostamento di 120° in una frazione di uno spostamento di 90°.
IL HueShift
il parametro deve essere un valore compreso tra -1..1
.
Ad esempio, per applicare uno spostamento di 60°:
- cambiando il rosso in giallo,
- cambiando il giallo in verde
- cambiando il verde in ciano
- cambiando il ciano in blu
- cambiando il blu in magenta
- cambiando il magenta in rosso
chiami:
var cm = GetHueShiftColorMatrix(60/360); //a value between 0..1
Implementazione:
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;
Nota:Qualsiasi codice viene rilasciato nel pubblico dominio.Nessuna attribuzione richiesta.
www.aforgenet.com potrebbe aiutare
I costruire un metodo che implementa il codice @IanBoid in linguaggio C #.
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
);
}