Implémentation de l'algorithme Retinex
-
20-12-2019 - |
Question
Je dois mettre en œuvre Algorithme rétinex à échelle unique et rétinex multi-échelles en C#,
J'ai cherché un peu mais je n'ai trouvé aucun projet pratique ni article utile avec du code
Si j'ai bien compris, je devrais :
- Convertir RVB en YUV
- Flou l'image à l'aide du filtre de flou gaussien
- Utilisez I'(x, y) = 255*log10( I(x, y)/G(x, y) ) + 127,5
I - est l'éclairage, G - le noyau gaussien, I' - l'image résultante - Сonvertir YUV en RVB
Ce code ne fonctionne pas correctement
public static Image<Bgr, byte> SingleScaleRetinex(this Image<Bgr, byte> img, int gaussianKernelSize, double sigma)
{
var radius = gaussianKernelSize / 2;
var kernelSize = 2 * radius + 1;
var ycc = img.Convert<Ycc, byte>();
var sum = 0f;
var gaussKernel = new float[kernelSize * kernelSize];
for (int i = -radius, k = 0; i <= radius; i++, k++)
{
for (int j = -radius; j <= radius; j++)
{
var val = (float)Math.Exp(-(i * i + j * j) / (sigma * sigma));
gaussKernel[k] = val;
sum += val;
}
}
for (int i = 0; i < gaussKernel.Length; i++)
gaussKernel[i] /= sum;
var gray = new Image<Gray, byte>(ycc.Size);
CvInvoke.cvSetImageCOI(ycc, 1);
CvInvoke.cvCopy(ycc, gray, IntPtr.Zero);
// Размеры изображения
var width = img.Width;
var height = img.Height;
var bmp = gray.Bitmap;
var bitmapData = bmp.LockBits(new Rectangle(Point.Empty, gray.Size), ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed);
unsafe
{
for (var y = 0; y < height; y++)
{
var row = (byte*)bitmapData.Scan0 + y * bitmapData.Stride;
for (var x = 0; x < width; x++)
{
var color = row + x;
float val = 0;
for (int i = -radius, k = 0; i <= radius; i++, k++)
{
var ii = y + i;
if (ii < 0) ii = 0; if (ii >= height) ii = height - 1;
var row2 = (byte*)bitmapData.Scan0 + ii * bitmapData.Stride;
for (int j = -radius; j <= radius; j++)
{
var jj = x + j;
if (jj < 0) jj = 0; if (jj >= width) jj = width - 1;
val += *(row2 + jj) * gaussKernel[k];
}
}
var newColor = 127.5 + 255 * Math.Log(*color / val);
if (newColor > 255)
newColor = 255;
else if (newColor < 0)
newColor = 0;
*color = (byte)newColor;
}
}
}
bmp.UnlockBits(bitmapData);
CvInvoke.cvCopy(gray, ycc, IntPtr.Zero);
CvInvoke.cvSetImageCOI(ycc, 0);
return ycc.Convert<Bgr, byte>();
}
La solution
Regardez: http://www.fer.unizg.h/ipg/resources/color_concstancy
Ces algorithmes sont des modifications de l'algorithme de rétinex (avec une amélioration de la vitesse), bien que l'auteur leur a donné des noms amusants :)
Il y a un code source complet (C ++, mais il est écrit très bien).
Autres conseils
Désolé pour le nécro-posting, mais il semble qu'il y ait une erreur à l'étape 3 de votre procédure qui peut induire en erreur quelqu'un de passage.
Afin d'appliquer la correction, vous souhaitez diviser l'image source par une copie filtrée par Gauss, et non par le noyau gaussien lui-même.En gros, en pseudo-code :
I_filtered(x,y) = G(x,y) * I(x,y)
I'(x,y) = log(I(x,y) / I_filtered(x,y))
Et puis appliquez le casting de I'(x,y)
au type numérique requis (uint8
, comme je peux me référer au message original).
Plus d'informations sur ce sujet peuvent être trouvées dans ce papier:
Ri(x, y) = log(Ii(x, y)) − log(Ii(x, y) ∗ F(x, y))
où
Ii
est l'image d'entrée sur le i-ième canal de couleur,Ri
est l'image de sortie Retinex sur le i -th canal etF
est la fonction surround normalisée..