Capítulo 8 Procesamiento digital de imágenes

Textos Universitarios / Serie Docencia ________________________________________________________________________ Capítulo 8 Procesamiento digital de i

0 downloads 98 Views 2MB Size

Recommend Stories


Procesamiento Digital de Imágenes
Procesamiento Digital de Imágenes Apuntes del curso impartido por el Dr. Boris Escalante Ramírez Agosto, 2006 2. 2.1. Fundamentos de la Imagen Digit

3 Procesamiento digital de
3 Procesamiento imágenes digital de Una vez preparada la escena para ser capturada por las cámaras de Visión Artificial, se deberá de tratar el as

Capítulo 12: Introducción al Procesamiento digital de Señales
UNIVERSIDAD POLITÉCNICA DE MADRID DEPARTAMENTO DE ELECTRÓNICA, AUTOMÁTICA E INFORMÁTICA INDUSTRIAL Capítulo 12: Introducción al Procesamiento digital

Arrocera y vaporera digital de 8 tazas
www.AromaCo.com ARC-978 For ARC-978, ARC-978B, ARC-978S & ARC-978SB Arrocera y vaporera digital de 8 tazas Manual de instrucciones Instruction Man

8-Cup Digital Rice Cooker & Food Steamer
www.AromaCo.com ARC-838TC Arrocera y vaporera digital de 8 tazas Manual de instrucciones Instruction Manual 8-Cup Digital Rice Cooker & Food Stea

Story Transcript

Textos Universitarios / Serie Docencia ________________________________________________________________________

Capítulo 8 Procesamiento digital de imágenes 8.1 Introducción El procesamiento digital de imágenes aparece tardíamente en la historia de la computación, ya que antes de pensar en ello, había que desarrollar el hardware y los sistemas operativos gráficos que permitieran hacerlo. Por otro lado, los algoritmos y las técnicas de optimización que han tenido que desarrollarse para el procesamiento digital de imágenes son muy sofisticados y elaborados. En la actualidad existen muchas aplicaciones de software que permiten el procesamiento digital de imágenes, mucho de este utiliza técnicas o algoritmos que son bien conocidos por la comunidad que trabaja en ello, pero otros utilizan sus propias variantes o técnicas nuevas que están poco documentadas. En este capítulo veremos diferentes técnicas que existen para procesar imágenes, estas técnicas podemos agruparlas en tres grandes grupos: • • •

Modificación de Color Modificación de Imagen Generación de efectos.

Después de explicar en que consisten, presentaremos el código de la clase gImage en la que se han definido varios métodos para que el usuario pueda procesarlas mediante un programa genérico que permite subir imágenes a un servidor Web.

8.1.1 Bitmaps (mapas de bits) La manera básica y original de representar una imagen digital con color en la memoria de la computadora es un bitmap. Un bitmap esta formado por filas de pixeles, donde cada uno en particular tiene un valor que determina su color. Este valor esta formado por tres números en el rango 0 a 255, asociados a los colores primarios Rojo, Verde y Azul. Cualquier color visible al ojo humano puede representarse de esta manera. Por ejemplo el color negro se codifica como R=0, V=0, A=0 y el color blanco (R,V,A) = (255,255,255). Desde este punto de vista, una imagen es un arreglo bidimensional de pixeles cada uno codificado en 3 bytes que puede tener 256x256x256=16.8 millones de diferentes colores. Esta técnica se conoce como codificación RGB y está adaptada a la visión humana. Sin embargo hay otras técnicas de codificación donde las cámaras o dispositivos de medición juegan un papel predominante. El rango de 0 a 255 se acordó por dos razones. La primera debido a que el ojo humano no es lo suficientemente sensible como para diferenciar más de 256 niveles de intensidad para un color y por otro lado es la capacidad de almacenamiento para un byte desde el punto de vista de la computación.

375

Jenaro C. Paz ________________________________________________________________________

8.1.2 Representación vectorial de los colores Como hemos mencionado, en un mapa de bits, los colores se codifican en tres bytes representando su descomposición en los tres colores primarios. Matemáticamente puede interpretarse un color como un vector en el espacio tridimensional de Rojo, Verde y Azul. Bajo esta interpretación pueden aplicarse algunos conceptos de la geometría analítica en el tratamiento de colores y en la generación de filtros o transformaciones.

Figura 8.1 Espacio tridimensional de colores

Una imagen es una codificación en un dominio espacial bidimensional estático y esto nos permite que podamos contar con nuevas imágenes a partir de las originales sin tener que modificarlas haciendo uso de transformaciones o filtros aplicados a sus pixeles. Si consideramos una imagen con resolución de 512 x 384 pixeles, su almacenamiento sin compresión será en 590 Kbytes y otra de 2592 x 1728 pixeles estará almacenada en 13.4 Mbytes. Con técnicas de compresión, esta ultima puede almacenarse en un archivo de 2.9 Mbytes. Para las transformaciones y filtros que aplicaremos estaremos tratando con las imágenes, con esos espacios bidimensionales, que difieren mucho de los diferentes formatos de archivos en que las podemos almacenar en un disco duro por ejemplo. Para acceder a los datos de una imagen Bitmap a continuación presentamos la clase BitmapData que utilizaremos un poco más adelante en diferentes aplicaciones en el procesamiento digital de imágenes.

8.1.3 Clase BitmapData34 Requisitos Espacio de nombres: System.Drawing.Imaging 34

http://msdn2.microsoft.com/en-us/library/system.drawing.imaging.bitmapdata(VS.80).aspx Junio 3 de 2006

376

Textos Universitarios / Serie Docencia ________________________________________________________________________ Especifica los atributos de una imagen de mapa de bits. La clase BitmapData la utilizan los métodos LockBits y UnlockBits de la clase Bitmap. No puede heredarse.

8.1.3.1 Constructores públicos BitmapData (Constructor)

Inicializa una nueva instancia de la clase BitmapData.

8.1.3.2 Propiedades públicas Height

PixelFormat

Reserved Scan0

Stride

Width

Obtiene o establece el alto en píxeles del objeto Bitmap. A veces se denomina número de líneas de exploración. Obtiene o establece el formato de la información de píxeles en el objeto Bitmap que este objeto BitmapData devuelve. Reservado. No utilizar. Obtiene o establece la dirección de los datos del primer píxel en el mapa de bits. También corresponde a la primera línea de exploración del mapa de bits. Obtiene o establece el ancho de paso (también denominado ancho de exploración) del objeto Bitmap. Obtiene o establece el ancho en píxeles del objeto Bitmap. También corresponde al número de píxeles de una línea de exploración.

8.1.3.3 Métodos públicos Equals (se hereda de Object) GetHashCode (se hereda de Object)

GetType (se hereda de Object) ToString (se hereda de Object)

Sobrecargado. Determina si dos instancias de Object son iguales. Sirve como función hash para un tipo concreto, apropiado para su utilización en algoritmos de hash y estructuras de datos como las tablas hash. Obtiene el objeto Type de la instancia actual. Devuelve un objeto String que representa al objeto Object actual.

8.1.3.4 Métodos protegidos

377

Jenaro C. Paz ________________________________________________________________________ Reemplazado. Permite que un objeto Object intente liberar recursos y realizar otras operaciones de limpieza antes de que el objeto Object sea reclamado por el recolector de elementos no utilizados. En C# y C++, los finalizadores se expresan mediante la sintaxis del destructor. MemberwiseClone (se hereda de Object) Crea una copia superficial del objeto Object actual. Finalize (se hereda de Object)

8.2 Procesamiento de Imágenes 8.2.1 Modificación de Color En esta sección cubriremos algunas de las técnicas más conocidas que están relacionadas con la modificación que se hace a los pixeles de una imagen sin que estos cambien de posición.

8.2.1.1 Detección de orillas De lo mencionado en párrafos anteriores, podemos cuantificar la diferencia entre dos colores calculando la distancia geométrica entre los vectores que los representan. Consideremos dos colores C1 = (R1, G1, B1) y C2 = (R2, G2, B2), la distancia entre ellos esta dada por la fórmula:

El objetivo en la detección de orillas es determinar las orillas de las formas en una imagen y ser capaz de dibujar un bitmap resultante donde las orillas están en blanco sobre un fondo negro. La idea es muy sencilla, nos desplazamos por la imagen pixel por pixel comparando el color de cada uno con su vecino de la derecha y de abajo. Si alguna de estas comparaciones resulta en una diferencia muy grande el pixel estudiado es parte de una orilla y debe ponerse en blanco, de otra manera se pone en negro. Para llevar a cabo la comparación de colores entre pixeles lo haremos mediante apuntadores, ya que .Net no cuenta con un método que permita acceder a los datos de un píxel en forma directa.

Figura 8.2. Representación de una imagen en memoria

Refiriéndonos a la figura anterior, BitmapData es la clase que mediante sus métodos Stride y Scan0 nos permitirá acceder a la información de la imagen en cuestión.

378

Textos Universitarios / Serie Docencia ________________________________________________________________________

Figura 8.3. Representación de una imagen de dimensión Width x Height

A continuación se presenta la Clase gImage que iremos agrandando con nuevos métodos según vayamos avanzando en los diferentes temas asociados con el procesamiento digital de imágenes. El primer método que se ha incluido es EdgeDetect que se utiliza para la detección de orillas. Para entender el funcionamiento del mismo se hace uso de las figuras 8.2 y 8.3, donde se inicia haciendo un recorrido por todos los bytes de la imagen renglón por renglón y columna por columna. Tenga en cuenta que cada vez que termina un renglón hay que avanzar nOffset bytes para acceder al siguiente y así sucesivamente.

gImage.cs

using using using using

System; System.Drawing; System.Drawing.Imaging; System.Drawing.Drawing2D;

namespace JCPGraphics { /// /// Summary description for gImage. /// public class gImage { public gImage() { // // TODO: Add constructor logic here // } public static Bitmap EdgeDetect(Bitmap curImage, byte nThreshold)

379

Jenaro C. Paz ________________________________________________________________________ { Bitmap bClone = (Bitmap) curImage.Clone(); BitmapData bmData = curImage.LockBits(new Rectangle(0, 0, curImage.Width, curImage.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); BitmapData bmDataC = bClone.LockBits(new Rectangle(0, 0, curImage.Width, curImage.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); //stride = ancho de una linea de pixeles int stride = bmData.Stride; //Direccion de los datos del primer pixel System.IntPtr Scan0 = bmData.Scan0; System.IntPtr Scan0C = bmDataC.Scan0; double cRed,cGreen,cBlue; unsafe { byte * p = (byte *)Scan0; byte * p2 = (byte *)Scan0C; int nWidth = curImage.Width * 3; int nOffset = stride - nWidth; //

// 3 bytes por pixel

|-------------pixeles----------------|--nOffset--|

int nPixel1 = 0, nPixel2 = 0; for(int y=0;y=nThreshold)|| (nPixel2 >= nThreshold)) nPixel1 = 255; else nPixel1 = 0; p[0] = p[1]=p[2]=(byte) nPixel1; p++; p2++; } p +=nOffset; p2 +=nOffset; } } curImage.UnlockBits(bmData); bClone.UnlockBits(bmDataC);

380

Textos Universitarios / Serie Docencia ________________________________________________________________________ return curImage; } } }

Para hacer uso de esta biblioteca implementamos una forma Web como la siguiente:

Figura 8.4. Forma Web para subir una imagen al servidor IIS

Que nos permitirá hacer la búsqueda de una imagen en la computadora del usuario y luego subirla al servidor Web.

UploadEdgeDetection.aspx

WebForm1 Jpg and Png File Process (Edge Detection)

381

Jenaro C. Paz ________________________________________________________________________ Image file to upload to the server: Scale to specific size: { Width:Height:

UploadEdgeDetection.aspx.cs using using using using

382

System; System.Collections; System.ComponentModel; System.Data;

Textos Universitarios / Serie Docencia ________________________________________________________________________ using using using using using using using using using using

System.Drawing; System.Drawing.Drawing2D; System.Drawing.Imaging; System.Web; System.Web.SessionState; System.Web.UI; System.Web.UI.WebControls; System.Web.UI.HtmlControls; System.IO ; System.IO.IsolatedStorage;

namespace JCPGraphics { /// /// Summary description for WebForm1. /// public class UploadEdgeDetection : System.Web.UI.Page { public static string strFileName; public static string strFilePath; public static string newStrFilePath; public static string strFolder; public static string mimeType; public static string fileExt; protected System.Web.UI.WebControls.Button btnUpload; protected System.Web.UI.WebControls.Label lblUploadResult; protected System.Web.UI.WebControls.Label Label1; protected System.Web.UI.WebControls.Image Image1; protected System.Web.UI.WebControls.Button btnProcess; protected System.Web.UI.WebControls.Image Image2; protected System.Web.UI.WebControls.Label Label2; protected System.Web.UI.WebControls.Label Label3; protected System.Web.UI.WebControls.Label Label4; protected System.Web.UI.WebControls.Label Label5; protected System.Web.UI.WebControls.TextBox txtWidth; protected System.Web.UI.WebControls.TextBox txtHeight; protected System.Web.UI.WebControls.Label Label6; protected System.Web.UI.HtmlControls.HtmlGenericControl Header; protected System.Web.UI.HtmlControls.HtmlInputFile oFile; private void Page_Load(object sender, System.EventArgs e) { if(!IsPostBack) { File.Delete ("C:/TEMP/Image01.JPG"); File.Delete ("C:/TEMP/Image02.JPG"); File.Delete ("C:/TEMP/Image01.PNG"); File.Delete ("C:/TEMP/Image02.PNG"); } } #region Web Form Designer generated code override protected void OnInit(EventArgs e) { // // CODEGEN: This call is required by the ASP.NET Web Form Designer.

383

Jenaro C. Paz ________________________________________________________________________ // InitializeComponent(); base.OnInit(e); } /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.btnUpload.Click += new System.EventHandler(this.btnUpload_Click); this.btnProcess.Click += new System.EventHandler(this.btnProcess_Click); this.Load += new System.EventHandler(this.Page_Load); } #endregion private void btnUpload_Click(object sender, System.EventArgs e) { if(oFile.Value == "") { lblUploadResult.Text = "Click 'Browse' to select the file to upload."; } else { strFolder = "C:\\TEMP\\" ; // Retrieve the name of the file that is posted. strFileName = oFile.PostedFile.FileName; // Retrieve the MIME Type of the file that is posted. mimeType =oFile.PostedFile.ContentType; if(mimeType !="image/pjpeg" && mimeType !="image/x-png") { lblUploadResult.Text = strFileName + " is not a valid image File!"; } else { // Just take the name of the File strFileName = Path.GetFileName(strFileName); if(!Directory.Exists(strFolder)) { Directory.CreateDirectory(strFolder); } strFilePath = strFolder + strFileName; if(File.Exists(strFilePath)) { lblUploadResult.Text = strFileName + " already exists on the server!"; //lblUploadResult.Visible = true; } else

384

Textos Universitarios / Serie Docencia ________________________________________________________________________ { if (mimeType=="image/pjpeg" ) { newStrFilePath=strFolder+"Image01.jpg"; } if (mimeType=="image/x-png" ) { newStrFilePath=strFolder+"Image01.png"; } oFile.PostedFile.SaveAs(newStrFilePath); lblUploadResult.Text = strFileName + " has been successfully uploaded."; Image1.ImageUrl=newStrFilePath; Image1.Visible=true; btnProcess.Visible =true; Label3.Visible=true; Label4.Visible=true; Label5.Visible=true; Label6.Visible=true; txtWidth.Visible =true; txtHeight.Visible =true; Header.Visible =false; Bitmap curImage =new Bitmap(newStrFilePath); txtWidth.Text=""+curImage.Width ; txtHeight.Text=""+curImage.Height; } } } lblUploadResult.Visible = true; }

private void btnProcess_Click(object sender, System.EventArgs e) { btnProcess.Visible =false; int w1= UInt16.Parse(txtWidth.Text ); int h1= UInt16.Parse(txtHeight.Text ); Bitmap uploadImage =new Bitmap(newStrFilePath); //////////////////////////////////////////////////////////////// uploadImage=gImage.EdgeDetect1(uploadImage,50); /////////////////////////////////////////////////////////////// if (mimeType=="image/pjpeg" ) { newStrFilePath=strFolder+"Image02.jpg"; uploadImage.Save(newStrFilePath,ImageFormat.Jpeg); } if (mimeType=="image/x-png" ) { newStrFilePath=strFolder+"Image02.png"; uploadImage.Save(newStrFilePath,ImageFormat.Png); } Image2.ImageUrl=newStrFilePath; Image2.Visible=true; Image1.Visible=false;

385

Jenaro C. Paz ________________________________________________________________________ } } }

Una vez que el usuario selecciona una imagen de su computadora, ésta es enviada al servidor y al accionar el botón para procesarla se obtiene un resultado como el mostrado a continuación.

Figura 8.5. EdgeDetect

8.2.1.2 Escala de grises (grayscale) En el espacio de colores, los vectores en la dirección del vector (1, 1, 1) representan diferentes tonalidades de gris. Así, cualquier píxel (r, g, b) de una imagen, proyectado sobre este vector nos dará su contribución gris a una nueva imagen que formemos con todas las proyecciones de los pixeles originales.

Figura 8.6. Espacio vectorial de Colores

Del álgebra de vectores sabemos que el producto escalar:

386

Textos Universitarios / Serie Docencia ________________________________________________________________________

Que nos da al calcular las magnitudes de los vectores:

Pero como la proyección de C en la dirección de u es:

Entonces:

El mayor valor que puede tomar esta expresión es 255 √3 y como debemos cuidar que la magnitud de esta expresión nunca rebase 255 debemos normalizarla multiplicando por 1/ √3 Así:

Es la proyección normalizada de un píxel en la dirección de los grises. Teniendo esto en mente, haremos un método para recorrer todos los pixeles de una imagen para generar su componente gris. gImage.GrayscaleNormalized public static Bitmap GrayScaleNormalized(Bitmap curImage) { BitmapData imgData = curImage.LockBits(new Rectangle(0, 0,curImage.Width, curImage.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); int stride = imgData.Stride; System.IntPtr Scan0 = imgData.Scan0; unsafe { byte * p = (byte *)(void *)Scan0; byte byteBlue, byteGreen, byteRed; int nOffset = stride - curImage.Width *3; int nHeight = curImage.Height; int nWidth= curImage.Width; for(int y = 0; y < nHeight; y++)

387

Jenaro C. Paz ________________________________________________________________________ { for (int x = 0; x < nWidth; x++) { byteBlue = p[0]; byteGreen = p[1]; byteRed = p[2]; p[0]= p[1]=p[2]=((byte)(byteRed + byteGreen + byteBlue))/3; p+=3; } p+=nOffset; } } curImage.UnlockBits (imgData); return curImage; }

Si en la forma Web que utilizamos en el ejercicio anterior en vez de uploadImage=gImage.EdgeDetect1(uploadImage,50);

utilizamos uploadImage=gImage.GrayscaleNormalized(uploadImage);

para procesar una imagen a color obtendremos una nueva imagen en gris como la mostrada a continuación.

Figura 8.7. GrayscaleNormalized

8.2.1.3 Inversión El valor mas grande que puede tomar un color es 255 y el mas pequeño 0, entonces si deseamos invertir las contribuciones de los diferentes pixeles a la formación de una

388

Textos Universitarios / Serie Docencia ________________________________________________________________________ imagen, debemos restar su color de 255 y esta diferencia tomarla como la contribución al color de la nueva imagen. En la figura 8.8 se observa una gráfica entre la señal de entrada y la de salida en el caso de la inversión.

Figura 8.8. Fórmula para la Inversión de Colores

Teniendo esto en mente, haremos un método para recorrer todos los pixeles de una imagen y generaremos su correspondiente imagen invertida.

gImage.Invert

public static Bitmap Invert(Bitmap curImage) { BitmapData imgData = curImage.LockBits(new Rectangle(0,0,curImage.Width, curImage.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); int stride = imgData.Stride; System.IntPtr Scan0 = imgData.Scan0; unsafe { byte *p = (byte *)Scan0; int nHeight = curImage.Height; int nWidth= curImage.Width *3; int nOffset = stride - nWidth; for(int y = 0; y < nHeight; y++) { for (int x = 0; x < nWidth; x++) { p[0] = (byte)(255-p[0]); p++; } p+=nOffset; } }

389

Jenaro C. Paz ________________________________________________________________________ curImage.UnlockBits (imgData); return curImage; }

Si en la forma Web que utilizamos en el ejercicio anterior en vez de uploadImage=gImage.GrayscaleNormalized(uploadImage);

utilizamos uploadImage=gImage.Invert(uploadImage);

para procesar una imagen a color obtendremos una nueva imagen invertida como la mostrada a continuación.

Figura 8.9. Invert

8.2.1.4 Brillo Aumentar el brillo de una imagen consiste en sumar o restar una constante a los colores que constituyen un píxel, cuidando siempre de nunca rebasar los límites 0 y 255. Si observamos la siguiente figura, aumentar o disminuir el brillo en una imagen consiste en aumentar o disminuir la ordenada al origen de la línea recta con pendiente a 45 grados que representa los grises.

390

Textos Universitarios / Serie Docencia ________________________________________________________________________

Figura 8.10. Aumento o disminución de Brillo

Teniendo esto en mente, haremos un método para recorrer todos los pixeles de una imagen y generaremos su correspondiente imagen donde hemos aumentado o disminuido el brillo gImage.Brightness

public static Bitmap Brightness(Bitmap curImage, int nBrightness) { if (nBrightness < -255 ) nBrightness = -10; if (nBrightness > 255 ) nBrightness = 10; BitmapData imgData = curImage.LockBits(new Rectangle(0, 0, curImage.Width, curImage.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); int stride = imgData.Stride; System.IntPtr Scan0 = imgData.Scan0; int []Bright_transform = new int [256]; for(int i=0; i 255) if(Bright_transform[i] < 0 ) }

Bright_transform[i] =255; Bright_transform[i] =0;

unsafe { byte * p = (byte *)Scan0; int nOffset = stride - curImage.Width*3; int nWidth = curImage.Width * 3; int nHeight = curImage.Height; for(int y=0;y 255) cvalue = 255; p[1] = (byte) cvalue; cvalue = 128+(nred-128)*contrast; if (cvalue < 0) cvalue = 0; if (cvalue > 255) cvalue = 255; p[2] = (byte) cvalue; p += 3; } p += nOffset; } } curImage.UnlockBits(imgData); return curImage; }

Si en la forma Web que utilizamos en el ejercicio anterior en vez de uploadImage=gImage.Brithness(uploadImage,60);

utilizamos uploadImage=gImage.Contrast(uploadImage,80);

para procesar una imagen a color obtendremos una nueva imagen donde el contraste se ha aumentado considerablemente.

Figura 8.13. Contrast

394

Textos Universitarios / Serie Docencia ________________________________________________________________________

8.2.1.6 Modificación de Colores En esta sección estamos interesados en variar para cada uno de los pixeles que constituyen una imagen, las contribuciones en rojo, verde y azul en cantidades constantes de tal manera que podamos resaltar los rojos y disminuir los azules por ejemplo. Teniendo lo anterior en cuenta, haremos un método para recorrer todos los pixeles de una imagen y generaremos su correspondiente imagen donde hemos aumentado o disminuido cada uno de los colores en cierta cantidad.

gImage.ModifyColor

public static Bitmap ModifyColor(Bitmap curImage, int nred, int ngreen, int nblue) { if (nred < -255 || nred > 255) nred=0; if (ngreen < -255 || ngreen > 255) ngreen=0; if (nblue < -255 || nblue > 255) nblue=0; int nWidth = curImage.Width; int nHeight = curImage.Height; BitmapData imgData = curImage.LockBits(new Rectangle(0, 0, nWidth, nHeight), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); int stride = imgData.Stride; System.IntPtr Scan0 = imgData.Scan0; unsafe { byte * p = (byte *)Scan0; int nOffset = stride - curImage.Width*3; int nPixel; for(int y=0;y 1 hay una gran corrección en el contraste para valores pequeños del color de entrada mientras que una pequeña corrección en el contraste para valores grandes. El brillo aumenta más para valores intermedios del color de entrada. Para gamma < 1 hay una pequeña corrección en el contraste para valores pequeños del color de entrada mientras que una gran corrección en el contraste para valores grandes. El brillo disminuye más para valores intermedios del color de entrada.

Teniendo lo anterior en cuenta, haremos un método para recorrer todos los pixeles de una imagen y generaremos su correspondiente imagen donde hemos aumentado o disminuido su gamma en cierta cantidad.

397

Jenaro C. Paz ________________________________________________________________________ gImage.Gamma public static Bitmap Gamma(Bitmap curImage, double g_red, double g_green, double g_blue) { if (g_red < .2 || g_red > 5) g_red=1.0; if (g_green < .2 || g_green > 5) g_green=1.0; if (g_blue < .2 || g_blue > 5) g_blue=1.0; byte [] redGamma = new byte [256]; byte [] greenGamma = new byte [256]; byte [] blueGamma = new byte [256]; for (int i = 0; i< 256; ++i) { redGamma[i] = (byte)Math.Min(255, (int)(( 255.0 * Math.Pow(i/255.0, 1.0/g_red)) + 0.5)); greenGamma[i] = (byte)Math.Min(255, (int)(( 255.0 * Math.Pow(i/255.0, 1.0/g_green)) + 0.5)); blueGamma[i] = (byte)Math.Min(255, (int)(( 255.0 * Math.Pow(i/255.0, 1.0/g_blue)) + 0.5)); } int nWidth=curImage.Width; int nHeight = curImage.Height; BitmapData imgData = curImage.LockBits(new Rectangle(0, 0, nWidth, nHeight), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); int stride = imgData.Stride; System.IntPtr Scan0 = imgData.Scan0; unsafe { byte * p = (byte *)(void *)Scan0; int nOffset = stride - curImage.Width*3; for(int y=0;y0) { for(int x=0;x

Get in touch

Social

© Copyright 2013 - 2024 MYDOKUMENT.COM - All rights reserved.