Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Imagenes Cap 8
Imagenes Cap 8
________________________________________________________________________
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.
375
Jenaro C. Paz
________________________________________________________________________
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.
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.
377
Jenaro C. Paz
________________________________________________________________________
378
Textos Universitarios / Serie Docencia
________________________________________________________________________
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 System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
namespace JCPGraphics
{
/// <summary>
/// Summary description for gImage.
/// </summary>
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);
unsafe
{
byte * p = (byte *)Scan0;
byte * p2 = (byte *)Scan0C;
int nWidth = curImage.Width * 3; // 3 bytes por pixel
int nOffset = stride - nWidth;
// |-------------pixeles----------------|--nOffset--|
nPixel1=(int)Math.Sqrt(cRed+cGreen+cBlue);
cRed=Math.Pow(Math.Abs(p2[0]-(p2+stride)[0]),2);
cGreen=Math.Pow(Math.Abs(p2[1]-(p2+stride)[1]),2);
cBlue=Math.Pow(Math.Abs(p2[2]-(p2+stride)[2]),2);
nPixel2=(int)Math.Sqrt(cRed+cGreen+cBlue);
if ((nPixel1>=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
381
Jenaro C. Paz
________________________________________________________________________
<div id="Header" runat="server">
<asp:label id="Label1" style="Z-INDEX: 100; LEFT: 24px; POSITION:
absolute; TOP: 48px" runat="server" Font-Bold="True"
ForeColor="MediumPurple">Image file to upload to the server:
</asp:label>
<INPUT id="oFile" style="Z-INDEX: 101; LEFT: 256px; WIDTH: 300px;
POSITION: absolute; TOP: 48px; HEIGHT: 22px" type="file"
size="68" name="oFile" runat="server">
<asp:button id="btnUpload" style="Z-INDEX: 117; LEFT: 584px; POSITION:
absolute; TOP: 48px" runat="server" text="Upload">
</asp:button>
</div>
<asp:label id="lblUploadResult" style="Z-INDEX: 104; LEFT: 24px;
POSITION: absolute; TOP: 80px" ForeColor="Red" Visible="False"
Runat="server">
</asp:label>
<asp:label id="Label3" style="Z-INDEX: 109; LEFT: 32px; POSITION:
absolute; TOP: 120px" runat="server" Font-Size="Medium"
Font-Bold="True" ForeColor="MediumPurple" Visible="False">Scale
to specific size:
</asp:label>
<asp:label id="Label6" style="Z-INDEX: 114; LEFT: 208px; POSITION:
absolute; TOP: 96px" runat="server" Font-Size="XX-Large"
Visible="False" Height="64px">{</asp:label>
<asp:label id="Label4" style="Z-INDEX: 110; LEFT: 232px; POSITION:
absolute; TOP: 104px" runat="server" Font-Bold="True"
Visible="False">Width:</asp:label><asp:label id="Label5"
style="Z-INDEX: 111; LEFT: 232px; POSITION: absolute; TOP: 136px"
runat="server" Font-Bold="True" visible="False">Height:</asp:label>
<asp:textbox id="txtWidth" style="Z-INDEX: 112; LEFT: 288px; POSITION:
absolute; TOP: 104px" runat="server" Width="64px"
Visible="False"></asp:textbox>
<asp:textbox id="txtHeight" style="Z-INDEX: 113; LEFT: 288px; POSITION:
absolute; TOP: 136px" runat="server" Width="64px"
Visible="False"></asp:textbox>
<asp:button id="btnProcess" style="Z-INDEX: 106; LEFT: 576px; POSITION:
absolute; TOP: 120px" runat="server" Visible="False"
Text="Process">
</asp:button>
</form>
<asp:image id="Image1" style="Z-INDEX: 105; LEFT: 24px; POSITION:
absolute; TOP: 168px" runat="server" Visible="False">
</asp:image>
<asp:image id="Image2" style="Z-INDEX: 107; LEFT: 24px; POSITION:
absolute; TOP: 168px" runat="server" Visible="False">
</asp:image>
</body>
</HTML>
UploadEdgeDetection.aspx.cs
using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
382
Textos Universitarios / Serie Docencia
________________________________________________________________________
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Web;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using System.IO ;
using System.IO.IsolatedStorage;
namespace JCPGraphics
{
/// <summary>
/// Summary description for WebForm1.
/// </summary>
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;
383
Jenaro C. Paz
________________________________________________________________________
//
InitializeComponent();
base.OnInit(e);
}
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
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
if(!Directory.Exists(strFolder))
{
Directory.CreateDirectory(strFolder);
}
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;
}
////////////////////////////////////////////////////////////////
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.
386
Textos Universitarios / Serie Docencia
________________________________________________________________________
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í:
Teniendo esto en mente, haremos un método para recorrer todos los pixeles de una
imagen para generar su componente gris.
gImage.GrayscaleNormalized
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;
}
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.
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.
Teniendo esto en mente, haremos un método para recorrer todos los pixeles de una
imagen y generaremos su correspondiente imagen invertida.
gImage.Invert
389
Jenaro C. Paz
________________________________________________________________________
curImage.UnlockBits (imgData);
return curImage;
}
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.
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
________________________________________________________________________
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
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<nHeight;y++)
{
for(int x=0; x < nWidth; x++ )
391
Jenaro C. Paz
________________________________________________________________________
{
p[0] = (byte)Bright_transform[p[0]];
++p;
}
p += nOffset;
}
}
curImage.UnlockBits(imgData);
return curImage;
}
.
Si en la forma Web que utilizamos en el ejercicio anterior en vez de
uploadImage=gImage.Invert(uploadImage);
utilizamos
uploadImage=gImage.Brightness(uploadImage,60);
para procesar una imagen a color obtendremos una nueva imagen con más brillo como la
mostrada a continuación.
8.2.1.5 Contraste
392
Textos Universitarios / Serie Docencia
________________________________________________________________________
Así, la fórmula que tenemos que aplicar en este tipo de transformación tiene la forma
siguiente:
Nótese que esta fórmula representa una familia de rectas que pasan por el punto
(128,128) con diferentes pendientes.
Teniendo lo anterior 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 contraste.
gImage.Contrast
393
Jenaro C. Paz
________________________________________________________________________
{
for(int x=0; x < nWidth; ++x )
{
nblue = p[0];
ngreen = p[1];
nred = p[2];
cvalue = 128 +(nblue-128)*contrast;
if (cvalue < 0) cvalue = 0;
if (cvalue > 255) cvalue = 255;
p[0] = (byte) cvalue;
cvalue = 128+(ngreen -128)*contrast;
if (cvalue < 0) cvalue = 0;
if (cvalue > 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;
}
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.
394
Textos Universitarios / Serie Docencia
________________________________________________________________________
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
unsafe
{
byte * p = (byte *)Scan0;
int nOffset = stride - curImage.Width*3;
int nPixel;
for(int y=0;y<nHeight;++y)
{
for(int x=0; x < nWidth; ++x )
{
nPixel = p[2] + nred;
nPixel = Math.Max(nPixel, 0);
p[2] = (byte)Math.Min(255, nPixel);
nPixel = p[1] + ngreen;
nPixel = Math.Max(nPixel, 0);
p[1] = (byte)Math.Min(255, nPixel);
nPixel = p[0] + nblue;
nPixel = Math.Max(nPixel, 0);
p[0] = (byte)Math.Min(255, nPixel);
p += 3;
}
395
Jenaro C. Paz
________________________________________________________________________
p += nOffset;
}
}
curImage.UnlockBits(imgData);
return curImage;
}
uploadImage=gImage.Contrast(uploadImage,80);
utilizamos
uploadImage=gImage.ModifyColor(uploadImage,10,-10,20);
para procesar una imagen a color obtendremos una nueva imagen donde hemos
modificado los colores rojos en 10 unidades, los colores verdes en -10 unidades y los
colores azules en 20 unidades.
396
Textos Universitarios / Serie Docencia
________________________________________________________________________
La fórmula que se utiliza para hacer esta corrección viene dada por:
Que al ser tabulada para diferentes valores de Gamma nos produce una gráfica como la
siguiente:
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
int nWidth=curImage.Width;
int nHeight = curImage.Height;
unsafe
{
byte * p = (byte *)(void *)Scan0;
int nOffset = stride - curImage.Width*3;
for(int y=0;y<nHeight;++y)
{
for(int x=0; x < nWidth; ++x )
{
p[2] = redGamma[ p[2] ];
p[1] = greenGamma[ p[1] ];
p[0] = blueGamma[ p[0] ];
p += 3;
}
p += nOffset;
}
}
curImage.UnlockBits(imgData);
return curImage;
}
398
Textos Universitarios / Serie Docencia
________________________________________________________________________
uploadImage=gImage.ModifyColor(uploadImage,10,-10,20);
utilizamos
uploadImage=gImage.Gamma(uploadImage,1.2,0.8,1.4);
para procesar una imagen a color obtendremos una nueva imagen donde hemos
modificado el coeficiente Gamma en los colores rojos en 1.2 unidades, en los colores
verdes en 0.8 unidades y en los colores azules en 1.4 unidades.
O bien esta otra donde hemos modificado el coeficiente Gamma en los colores rojo
verde y azul en 0.8 unidades.
399
Jenaro C. Paz
________________________________________________________________________
A continuación se presenta un método para recorrer todos los pixeles de una imagen
invirtiendolos verticalmente, hace uso de los métodos GetPixel y SetPixel de la Clase
Bitmap.
gImage.Flip
400
Textos Universitarios / Serie Docencia
________________________________________________________________________
uploadImage=gImage.Gamma(uploadImage,1.2,0.8,1.4);
utilizamos
uploadImage=gImage.Flip(uploadImage);
para procesar una imagen a color obtendremos una nueva imagen donde la hemos
invertido.
Esta modificación se lleva a cabo moviendo un píxel del renglón j, columna i al mismo
renglón j, columna Width-(i+1).
401
Jenaro C. Paz
________________________________________________________________________
A continuación se presenta un método para recorrer todos los pixeles de una imagen
invirtiendolos horizontalmente, también hace uso de los métodos GetPixel y SetPixel de
la Clase Bitmap.
gImage.Mirror
return curImage;
}
uploadImage=gImage.Flip(uploadImage);
utilizamos
uploadImage=gImage.Mirror(uploadImage);
para procesar una imagen a color obtendremos una nueva imagen espejo de la original
402
Textos Universitarios / Serie Docencia
________________________________________________________________________
gImage.Scale
if(bReduce)
{
for(int x=0;x<curImage.Width ; x++)
{
for(int y=0;y<curImage.Height ; y++)
curImage.SetPixel(x,y,bTemp.GetPixel((int)(Math.Floor(x*nXfactor)),
(int)(Math.Floor(y*nYfactor))));
}
}
else
{
double f_X, f_Y, diffX, diffY;
int up_X, up_Y, down_X, down_Y;
Color color1 = new Color();
Color color2 = new Color();
Color color3 = new Color();
Color color4 = new Color();
403
Jenaro C. Paz
________________________________________________________________________
byte red, green, blue;
byte b1, b2;
for (int x = 0; x < curImage.Width; ++x)
for (int y = 0; y < curImage.Height; ++y)
{
down_X = (int)Math.Floor(x * nXfactor);
down_Y = (int)Math.Floor(y * nYfactor);
up_X = down_X + 1;
if (up_X >= bTemp.Width) up_X = down_X;
up_Y = down_Y + 1;
if (up_Y >= bTemp.Height) up_Y = down_Y;
f_X = x * nXfactor - down_X;
f_Y = y * nYfactor - down_Y;
diffX = 1.0 - f_X;
diffY = 1.0 - f_Y;
color1 = bTemp.GetPixel(down_X, down_Y);
color2 = bTemp.GetPixel(up_X, down_Y);
color3 = bTemp.GetPixel(down_X, up_Y);
color4 = bTemp.GetPixel(up_X, up_Y);
curImage.SetPixel(x,y, System.Drawing.Color.FromArgb(255,
red, green, blue));
}
}
return curImage;
}
uploadImage=gImage.Mirror(uploadImage);
utilizamos
uploadImage=gImage.Scale(uploadImage,100,67,true);
para procesar una imagen a color obtendremos una nueva imagen reducida de la original
404
Textos Universitarios / Serie Docencia
________________________________________________________________________
gImage.Rotate
Graphics g = Graphics.FromImage(pixelImage);
g.Clear(System.Drawing.Color.White);
rae z X = new rae z();
Rectangle toCenterImage = new Rectangle(
(newCanvasSize-curImage.Width)/2,
(newCanvasSize-curImage.Height)/2,
curImage.Width ,
curImage.Height );
Point pCenterOfCanvas = new Point(newCanvasSize/2,
newCanvasSize/2);
X.RotateAt(angle,pCenterOfCanvas,MatrixOrder.Append);
g.Transform=X;
g.DrawImage(curImage,toCenterImage);
return pixelImage;
405
Jenaro C. Paz
________________________________________________________________________
uploadImage=gImage.Scale(uploadImage,100,67, rae);
utilizamos
uploadImage=gImage.Rotate(uploadImage,30);
para procesar una imagen a color obtendremos una nueva imagen donde hemos girado la
imagen original en 30 grados en sentido de las manecillas del reloj.
406
Textos Universitarios / Serie Docencia
________________________________________________________________________
Y la relación entre las Y’s y X’s del rectángulo con las Y’s del trapecio están dadas por:
Y la relación entre las Y’s y X’s del rectángulo con las Y’s del trapecio están dadas por:
407
Jenaro C. Paz
________________________________________________________________________
gImage.PerspectiveX
float b1 = (float)(Math.Abs(percentage*0.007*curImage.Width)) ;
float a1 = b1/2;
curImage = new Bitmap (curImage.Width, curImage.Height,
bTemp.PixelFormat );
Graphics g = Graphics.FromImage(curImage);
g.Clear(bgColor);
if(percentage >0)
{
for(int x=0;x<imWidth ; x++)
{
int x2 = (int)(x*(imWidth-b1)/imWidth);
//upper half
for(int y=0;y<imHeight/2 ; y++)
{
int y2 =(int)(y + a1*x2/(imWidth -b1)-
2*y*a1*x2/((imWidth -b1)*imHeight));
curImage.SetPixel(x2,y2,bTemp.GetPixel(x,y));
}
//lower half
for(int y=imHeight/2 ;y<imHeight; y++)
{
int y2 =(int)(y - a1*x2/(imWidth -b1)+ 2*(imHeight-
y)*a1*x2/((imWidth -b1)*imHeight));
curImage.SetPixel(x2,y2,bTemp.GetPixel(x,y));
}
}
}
else
{
for(int x=0;x<imWidth ; x++)
{
int x2 = (int)(b1+x*(imWidth-b1)/imWidth);
for(int y=0;y<imHeight/2 ; y++)
{
int y2 =(int)(y + a1*(imWidth-x2)/(imWidth -b1)-
2*y*a1*(imWidth-x2)/((imWidth -b1)*imHeight));
curImage.SetPixel(x2,y2,bTemp.GetPixel(x,y));
408
Textos Universitarios / Serie Docencia
________________________________________________________________________
}
for(int y=imHeight/2 ;y<imHeight; y++)
{
int y2 =(int)(y - a1*(imWidth-x2)/(imWidth -b1)+
2*(imHeight-y)*a1*(imWidth-x2)/
((imWidth -b1)*imHeight));
curImage.SetPixel(x2,y2,bTemp.GetPixel(x,y));
}
}
}
return curImage;
}
uploadImage=gImage.Rotate(uploadImage,30);
utilizamos
uploadImage=gImage.PerspectiveX(uploadImage,10,Color.White);
para procesar una imagen a color obtendremos una nueva imagen donde hemos realizado
una perspectiva horizontal modificado la imagen original en 10 % .
409
Jenaro C. Paz
________________________________________________________________________
Y la relación entre las X’s y Y’s del rectángulo con las X’s del trapecio están dadas por:
Y la relación entre las X’s y Y’s del rectángulo con las X’s del trapecio están dadas por:
410
Textos Universitarios / Serie Docencia
________________________________________________________________________
Para la parte izquierda de la imagen y por:
gImage.PerspectiveY
icol b1 = ( icol)(Math.Abs(percentage*0.005*curImage.Width)) ;
icol a1 = b1*2;
curImage = new Bitmap (curImage.Width, curImage.Height,
bTemp.PixelFormat );
Graphics g = Graphics.FromImage(curImage);
g.SmoothingMode=SmoothingMode.HighQuality;
g.Clear( icolor);
if(percentage >0)
{
for(int y=0;y<imHeight ; y++)
{
int y2 = (int)(y*(imHeight-a1)/imHeight);
// Left Side
for(int x=0;x<imWidth/2 ; x++)
{
int x2 =(int)(x + b1*y2/(imHeight –a1)-
2*x*b1*y2/((imHeight –a1)*imWidth));
curImage.SetPixel(x2,y2,bTemp.GetPixel(x,y));
}
//Right Side
for(int x=imWidth/2 ;x<imWidth; x++)
{
int x2 =(int)(x – b1*y2/(imHeight –a1)+ 2*(imWidth-
x)*b1*y2/((imHeight –a1)*imWidth));
curImage.SetPixel(x2,y2,bTemp.GetPixel(x,y));
}
}
}
else
{
for(int y=0;y<imHeight ; y++)
411
Jenaro C. Paz
________________________________________________________________________
{
int y2 = (int)(a1+y*(imHeight-a1)/imHeight);
for(int x=0;x<imWidth/2 ; x++)
{
int x2 =(int)(x + b1*(imHeight-y2)/(imHeight –a1)-
2*x*b1*(imHeight-y2)/((imHeight –a1)*imWidth));
curImage.SetPixel(x2,y2,bTemp.GetPixel(x,y));
}
for(int x=imWidth/2 ;x<imWidth; x++)
{
int x2 =(int)(x – b1*(imHeight-y2)/(imHeight –a1)+
2*(imWidth-x)*b1*(imHeight-y2)/
((imHeight –a1)*imWidth));
curImage.SetPixel(x2,y2,bTemp.GetPixel(x,y));
}
}
}
return curImage;
}
uploadImage=gImage.PerspectiveX(uploadImage,10,Color.White);
utilizamos
uploadImage=gImage.PerspectiveY(uploadImage,20,Color.White);
para procesar una imagen a color obtendremos una nueva imagen donde hemos realizado
una perspectiva vertical modificado la imagen original en 20 % .
412
Textos Universitarios / Serie Docencia
________________________________________________________________________
gImage.SkewHor
uploadImage=gImage.PerspectiveY(uploadImage,20,Color.White);
utilizamos
413
Jenaro C. Paz
________________________________________________________________________
uploadImage=gImage.SkewHor(uploadImage,30);
para procesar una imagen a color obtendremos una nueva imagen donde hemos realizado
una inclinación horizontal modificado la imagen original en un ángulo de 30 grados .
gImage.SkewVer
414
Textos Universitarios / Serie Docencia
________________________________________________________________________
Math.Tan(angle*Math.PI/180.0));
Bitmap pixelImage = new Bitmap(curImage.Width ,newCanvasHeight);
Graphics g = Graphics.FromImage(pixelImage);
g.Clear(Color.White);
Point[] pts =
{
new Point(0,0),
new Point(curImage.Width, (int)(curImage.Width *
Math.Tan(angle*Math.PI/180.0))),
new Point(0,curImage.Height)
};
g.DrawImage(curImage,pts);
return pixelImage;
}
uploadImage=gImage.SkewHor(uploadImage,30);
utilizamos
uploadImage=gImage.SkewVer(uploadImage,30);
para procesar una imagen a color obtendremos una nueva imagen donde hemos realizado
una inclinación vertical modificado la imagen original en un ángulo de 30 grados .
415
Jenaro C. Paz
________________________________________________________________________
1 1 1
1 4 1
1 1 1
Los kernels pueden tener tamaños arbitrarios, pero los de 3 x 3 son los más usados en la
mayoría de las situaciones (también debido a que son los mas rápidos), ya que solo toma
en consideración el valor del pixel mismo y el de sus 8 vecinos. Ahora, para aplicar este
kernel a una imagen debe colocarse el kernel sobre la imagen y multiplicar los valores de
color por 1 o 4. El resultado se suma y divide por 12, en este caso (la suma de los
elementos del kernel).
A continuación se presenta una sección de una imagen en grises, sobre la que se aplicará
el kernel de convolución.
La hemos escogido en grises para facilitar la explicación, ahora bien sobre cada uno de
los pixeles de esta sección de imagen, colocamos el valor de su color, teniendo como
entendido que por ejemplo 150 = (150, 150, 150) en sus componentes rojo, verde y azul.
416
Textos Universitarios / Serie Docencia
________________________________________________________________________
Ahora colocamos el kernel sobre los pixeles de la imagen y el primer pixel que
obtenemos es el (2,2), luego al desplazarnos con el kernel a la derecha el siguiente pixel
que obtendremos será el (2,3) y así sucesivamente.
Si se tratara de una imagen no de grises, esto mismo hay que hacer para cada una de las
tres componentes (R, V, A) que constituyen al pixel, originando 9 multiplicaciones 8
sumas y una división por un factor de tres para cada uno de los pixeles de la imagen que
se vayan a procesar.
Por ejemplo si contamos con una imagen de 512 x 384 pixeles hay que realizar alrededor
de 10. 5 millones de operaciones para obtener la imagen filtrada. Si nos fijamos bien, los
pixeles de la orilla no pueden ser procesados porque no cuentan con todos sus vecinos
para aplicar el algoritmo entonces se tienen que desechar.
A continuación se presenta un método que multiplica una imagen por una matriz de
Convolución de 3 x 3
417
Jenaro C. Paz
________________________________________________________________________
gImage.DoConvolution
unsafe
{
int nPixel;
(pSrc[8] * conMat.elements[0,2]) +
(pSrc[2 + stride] * conMat.elements[1,0] ) +
(pSrc[5 + stride] * conMat.elements[1,1] ) +
(pSrc[8 + stride] * conMat.elements[1,2] ) +
(pSrc[2 + stride2] * conMat.elements[2,0] ) +
(pSrc[5 + stride2] * conMat.elements[2,1]) +
(pSrc[8 + stride2] * conMat.elements[2,2] ))
/ conMat.Factor) + conMat.Offset);
418
Textos Universitarios / Serie Docencia
________________________________________________________________________
(pSrc[7] * conMat.elements[0,2]) +
(pSrc[1 + stride] * conMat.elements[1,0]) +
(pSrc[4 + stride] * conMat.elements[1,1]) +
(pSrc[7 + stride] * conMat.elements[1,2]) +
(pSrc[1 + stride2] * conMat.elements[2,0])
+
(pSrc[4 + stride2] * conMat.elements[2,1])
+
(pSrc[7 + stride2] * conMat.elements[2,2]))
/ conMat.Factor) + conMat.Offset);
p += 3;
pSrc += 3;
}
p += nOffset+6;
pSrc += nOffset+6;
}
}
curImage.UnlockBits(imgData);
bCopy.UnlockBits(bmCopy);
return curImage;
Hay que tener en mente que para cada matriz de convolución se tendrá un filtrado
diferente, es lo que veremos a continuación.
419
Jenaro C. Paz
________________________________________________________________________
1 2 1
2 nWeight 2
1 2 1
gImage.GaussianBlur
uploadImage=gImage.SkewVer(uploadImage,30);
utilizamos
uploadImage=gImage.GaussianBlur(uploadImage,8);
para procesar una imagen a color obtendremos una nueva imagen donde hemos realizado
un borrado Gaussiano modificado la imagen original usando un peso de 8 en la matriz de
convolución .
420
Textos Universitarios / Serie Docencia
________________________________________________________________________
2 0 0
0 -1 0
0 0 -1
gImage.Emboss
conMat.Offset = 127;
uploadImage=gImage.GaussianBlur(uploadImage,8);
utilizamos
uploadImage=gImage.Emboss(uploadImage);
para procesar una imagen a color, obtendremos una nueva imagen donde hemos realizado
un realzado modificado la imagen original.
421
Jenaro C. Paz
________________________________________________________________________
0 -1 0
-1 nWeight -1
0 -1 0
gImage.Sharpen
uploadImage=gImage.Emboss(uploadImage);
utilizamos
uploadImage=gImage.Sharpen(uploadImage,10);
para procesar una imagen a color, obtendremos una nueva imagen donde hemos realizado
una modificación a la imagen original por nitidez.
422
Textos Universitarios / Serie Docencia
________________________________________________________________________
gImage.Smooth
1 1 1
1 nWeight 1
1 1 1
uploadImage=gImage.Sharpen(uploadImage,10);
utilizamos
uploadImage=gImage.Smooth(uploadImage,4);
para procesar una imagen a color, obtendremos una nueva imagen donde hemos realizado
una modificación a la imagen original por suavizado.
423
Jenaro C. Paz
________________________________________________________________________
gImage.MeanRemoval
-1 -1 -1
-1 nWeight -1
-1 -1 -1
uploadImage=gImage.Smooth(uploadImage,4);
utilizamos
uploadImage=gImage.MeanRemoval(uploadImage,4);
para procesar una imagen a color, obtendremos una nueva imagen donde hemos realizado
una modificación a la imagen original por MeanRemoval.
424
Textos Universitarios / Serie Docencia
________________________________________________________________________
gImage.EdgeDetectionQuick
-1 -1 -1
0 0 0
1 1 1
conMat.Offset = 127;
uploadImage=gImage.MeanRemoval(uploadImage,4);
utilizamos
uploadImage=gImage.EdgeDetectionQuick(uploadImage,4);
para procesar una imagen a color, obtendremos una nueva imagen donde hemos realizado
una modificación a la imagen original por deteccion de orillas.
425
Jenaro C. Paz
________________________________________________________________________
1 0 -1
2 0 -2
1 0 -1
1 2 1
0 0 0
-1 -2 -1
gImage.EdgeDetectionConvolution
switch (nType)
{
case SOBEL_EDGE_DETECT:
conMat.initialize(0);
conMat.elements[0,0] = conMat.elements[2,0] = 1;
conMat.elements[0,2] = conMat.elements[2,2] = -1;
conMat.elements[1,0] = 2;
conMat.elements[1,2] = -2;
conMat.Offset = 0;
break;
case PREWITT_EDGE_DETECT:
conMat.initialize(0);
conMat.elements[0,0] = conMat.elements[1,0] =
conMat.elements[2,0] = 1;
conMat.elements[0,2] = conMat.elements[1,2] =
conMat.elements[2,2] = -1;
conMat.Offset = 0;
break;
case KIRSH_EDGE_DETECT:
conMat.initialize(-3);
conMat.elements[1,1] = 0;
conMat.elements[0,0] = conMat.elements[1,0] =
conMat.elements[2,0] = 5;
conMat.Offset = 0;
break;
}
426
Textos Universitarios / Serie Docencia
________________________________________________________________________
gImage.DoConvolution3x3(curImage, conMat);
switch (nType)
{
case SOBEL_EDGE_DETECT:
conMat.initialize(0);
conMat.elements[0,0] = conMat.elements[0,2] = 1;
conMat.elements[2,0] = conMat.elements[2,2] = -1;
conMat.elements[0,1] = 2;
conMat.elements[2,1] = -2;
conMat.Offset = 0;
break;
case PREWITT_EDGE_DETECT:
conMat.initialize(0);
conMat.elements[2,0] = conMat.elements[2,1] =
conMat.elements[2,2] = -1;
conMat.elements[0,0] = conMat.elements[0,1] =
conMat.elements[0,2] = 1;
conMat.Offset = 0;
break;
case KIRSH_EDGE_DETECT:
conMat.initialize(-3);
conMat.elements[1,1] = 0;
conMat.elements[0,0] = conMat.elements[0,1] =
conMat.elements[0,2] = 5;
conMat.Offset = 0;
break;
}
gImage.DoConvolution3x3(bTemp, conMat);
unsafe
{
byte * p = (byte *)(void *)Scan0;
byte * p2 = (byte *)(void *)Scan02;
int nPixel = 0;
for(int y=0;y<curImage.Height;++y)
{
for(int x=0; x < nWidth; ++x )
427
Jenaro C. Paz
________________________________________________________________________
{
nPixel = (int) Math.Sqrt((p[0]*p[0]) + (p2[0] *
p2[0]));
if (nPixel<nThreshold)nPixel = nThreshold;
if (nPixel>255) nPixel = 255;
p[0] = (byte) nPixel;
++p;
++p2;
}
p += nOffset;
p2 += nOffset;
}
}
curImage.UnlockBits(bmData);
bTemp.UnlockBits(bmData2);
return curImage;
}
uploadImage=gImage.EdgeDetectionQuick(uploadImage,4);
utilizamos
uploadImage=gImage. EdgeDetectionConvolution(uploadImage,
SOBEL_EDGE_DETECT, 200);
para procesar una imagen a color, obtendremos una nueva imagen donde hemos realizado
una modificación a la imagen original por deteccion de orillas Sobel.
428
Textos Universitarios / Serie Docencia
________________________________________________________________________
1 0 -1
1 0 -1
1 0 -1
1 1 1
0 0 0
-1 -1 -1
uploadImage=gImage.EdgeDetectionQuick(uploadImage,4);
utilizamos
uploadImage=gImage. EdgeDetectionConvolution(uploadImage,
PREWITT_EDGE_DETECT, 200);
para procesar una imagen a color, obtendremos una nueva imagen donde hemos realizado
una modificación a la imagen original por deteccion de orillas Prewitt.
5 -3 -3
5 0 -3
5 -3 -3
429
Jenaro C. Paz
________________________________________________________________________
Y luego otro kernel de 3 x3 como el siguiente:
5 5 5
-3 0 -3
-3 -3 -3
uploadImage=gImage.EdgeDetectionQuick(uploadImage,4);
utilizamos
uploadImage=gImage. EdgeDetectionConvolution(uploadImage,
KIRSH_EDGE_DETECT, 200);
para procesar una imagen a color, obtendremos una nueva imagen donde hemos realizado
una modificación a la imagen original por deteccion de orillas Prewitt.
430