455 lines
13 KiB
C#
455 lines
13 KiB
C#
using System;
|
|
using System.Drawing;
|
|
using System.IO;
|
|
using System.Drawing.Imaging;
|
|
using System.Runtime.InteropServices;
|
|
using System.Drawing.Drawing2D;
|
|
|
|
namespace MarketData.CNNProcessing
|
|
{
|
|
public class ImageHelper : IDisposable
|
|
{
|
|
private Bitmap bitmap = null;
|
|
private Graphics graphics=null;
|
|
private PointMapping pointMapping;
|
|
private int width;
|
|
private int height;
|
|
|
|
public ImageHelper()
|
|
{
|
|
}
|
|
|
|
public ImageHelper(ImageHelper imageHelper)
|
|
{
|
|
this.bitmap=Copy(imageHelper.bitmap);
|
|
width=bitmap.Width;
|
|
height=bitmap.Height;
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
DisposeAll();
|
|
}
|
|
|
|
private void DisposeAll()
|
|
{
|
|
if(null!=bitmap)
|
|
{
|
|
bitmap.Dispose();
|
|
bitmap=null;
|
|
}
|
|
if(null!=graphics)
|
|
{
|
|
graphics.Dispose();
|
|
graphics=null;
|
|
}
|
|
}
|
|
|
|
private Bitmap Copy(Bitmap bitmap)
|
|
{
|
|
Bitmap copy = new Bitmap(bitmap.Width, bitmap.Height);
|
|
|
|
for(int rowIndex=0;rowIndex<bitmap.Height;rowIndex++)
|
|
{
|
|
for(int colIndex=0;colIndex<bitmap.Width;colIndex++)
|
|
{
|
|
Color color=bitmap.GetPixel(rowIndex,colIndex);
|
|
copy.SetPixel(rowIndex,colIndex,color);
|
|
}
|
|
}
|
|
return copy;
|
|
}
|
|
|
|
public bool LoadImage(Stream stream)
|
|
{
|
|
try
|
|
{
|
|
DisposeAll();
|
|
Image image=Image.FromStream(stream);
|
|
bitmap = new Bitmap(image);
|
|
height = bitmap.Height;
|
|
width = bitmap.Width;
|
|
return true;
|
|
}
|
|
catch(Exception exception)
|
|
{
|
|
MDTrace.WriteLine(LogLevel.DEBUG,exception.ToString());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public bool LoadImage(string pathFileName)
|
|
{
|
|
Stream bitmapStream = null;
|
|
try
|
|
{
|
|
DisposeAll();
|
|
bitmap = new Bitmap(pathFileName);
|
|
width=bitmap.Width;
|
|
height=bitmap.Height;
|
|
return true;
|
|
}
|
|
catch(Exception exception)
|
|
{
|
|
MDTrace.WriteLine(LogLevel.DEBUG,exception.ToString());
|
|
return false;
|
|
}
|
|
finally
|
|
{
|
|
if(null!=bitmapStream)
|
|
{
|
|
bitmapStream.Close();
|
|
bitmapStream.Dispose();
|
|
bitmapStream.Dispose();
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Resize image to given width maintaining aspect ration for height.
|
|
/// If aspect ration is used for CNN then you will need to introduce padding. For instance 640,640 image and then paste the aspect
|
|
/// mainained image onto that. The CNN will then have to learn that black means nothing. Resizing is the recommended way to go.
|
|
/// </summary>
|
|
public bool Resize(int newWidth)
|
|
{
|
|
double aspectRatio=(double)bitmap.Width/(double)bitmap.Height;
|
|
int newHeight=(int)(((double)newWidth)/aspectRatio);
|
|
if(0!=newHeight%2)newHeight++;
|
|
return Resize(newWidth,newHeight);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Resize image to given width and height. The documenation on CNN's indicates that resizing will work better than maintaining apsect
|
|
/// ration with padding
|
|
/// </summary>
|
|
public bool Resize(int newWidth, int newHeight)
|
|
{
|
|
Graphics graphics = null;
|
|
ImageAttributes imageAttributes = null;
|
|
try
|
|
{
|
|
Validate();
|
|
Rectangle destRect=new Rectangle(0,0,newWidth,newHeight);
|
|
Bitmap destBitmap = new Bitmap(newWidth,newHeight);
|
|
|
|
destBitmap.SetResolution(bitmap.HorizontalResolution,bitmap.VerticalResolution);
|
|
graphics = Graphics.FromImage(destBitmap);
|
|
graphics.CompositingMode = CompositingMode.SourceCopy;
|
|
graphics.CompositingQuality = CompositingQuality.HighQuality;
|
|
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
|
|
graphics.SmoothingMode = SmoothingMode.HighQuality;
|
|
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
|
|
imageAttributes = new ImageAttributes();
|
|
imageAttributes.SetWrapMode(WrapMode.TileFlipXY);
|
|
graphics.DrawImage(bitmap, destRect, 0,0, bitmap.Width, bitmap.Height, GraphicsUnit.Pixel, imageAttributes);
|
|
bitmap.Dispose();
|
|
bitmap=destBitmap;
|
|
imageAttributes.Dispose();
|
|
graphics.Dispose();
|
|
return true;
|
|
}
|
|
catch(Exception exception)
|
|
{
|
|
MDTrace.WriteLine(LogLevel.DEBUG,exception.ToString());
|
|
return false;
|
|
}
|
|
finally
|
|
{
|
|
if(null!=graphics)
|
|
{
|
|
graphics.Dispose();
|
|
graphics=null;
|
|
}
|
|
if(null!=imageAttributes)
|
|
{
|
|
imageAttributes.Dispose();
|
|
imageAttributes=null;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void CreateImage(int width, int height,PointMapping pointMapping)
|
|
{
|
|
this.width=width;
|
|
this.height=height;
|
|
this.pointMapping=pointMapping;
|
|
bitmap=new Bitmap(width,height,System.Drawing.Imaging.PixelFormat.Format24bppRgb);
|
|
Validate();
|
|
graphics=Graphics.FromImage(bitmap);
|
|
}
|
|
|
|
// This is the method that is currently being used in the CNNClient.Predict to send the image stream to Flask
|
|
public Stream SaveBlackAndWhiteJPG()
|
|
{
|
|
Validate();
|
|
Bitmap bwBitmap=ToBlackAndWhite(bitmap);
|
|
Stream memoryStream=new MemoryStream();
|
|
BitmapExtensions.SaveJPG100(bwBitmap,memoryStream);
|
|
memoryStream.Position=0;
|
|
bwBitmap.Dispose();
|
|
return memoryStream;
|
|
}
|
|
|
|
// Convert bitmap to stream for use in CNNClient
|
|
public Stream ToStream()
|
|
{
|
|
Validate();
|
|
Stream memoryStream=new MemoryStream();
|
|
BitmapExtensions.SaveJPG100(bitmap,memoryStream);
|
|
memoryStream.Position=0;
|
|
return memoryStream;
|
|
}
|
|
|
|
public void SaveGrayScaleJPG(String pathFileName)
|
|
{
|
|
Validate();
|
|
Bitmap bwBitmap=ToGrayScale(bitmap);
|
|
Save(pathFileName,bwBitmap);
|
|
bwBitmap.Dispose();
|
|
}
|
|
|
|
// This is the method that is currently being used in the CNNProcessor to generate images
|
|
public void SaveBlackAndWhiteJPG(String pathFileName)
|
|
{
|
|
Validate();
|
|
Bitmap bwBitmap=ToBlackAndWhite(bitmap);
|
|
Save(pathFileName,bwBitmap);
|
|
bwBitmap.Dispose();
|
|
}
|
|
|
|
public void Save(String pathFileName)
|
|
{
|
|
Save(pathFileName,null);
|
|
}
|
|
|
|
private void Save(String pathFileName,Bitmap altBitmap=null)
|
|
{
|
|
Validate();
|
|
if(null==altBitmap)BitmapExtensions.SaveJPG100(bitmap,pathFileName);
|
|
else BitmapExtensions.SaveJPG100(altBitmap,pathFileName);
|
|
}
|
|
|
|
// Convert to 8 bits per pixel black and white
|
|
private Bitmap ToBlackAndWhite(Bitmap bmp)
|
|
{
|
|
Bitmap result = new Bitmap(bmp.Width, bmp.Height, System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
|
|
BitmapData data = result.LockBits(new Rectangle(0, 0, result.Width, result.Height), ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
|
|
byte[] bytes = new byte[data.Height * data.Stride];
|
|
Marshal.Copy(data.Scan0, bytes, 0, bytes.Length);
|
|
for (int y = 0; y < bmp.Height; y++)
|
|
{
|
|
for (int x = 0; x < bmp.Width; x++)
|
|
{
|
|
Color color = bmp.GetPixel(x, y);
|
|
byte rgb = (byte)((color.R + color.G + color.B) / 3);
|
|
if(rgb>0)rgb=255;
|
|
bytes[y * data.Stride + x] = rgb;
|
|
}
|
|
}
|
|
Marshal.Copy(bytes, 0, data.Scan0, bytes.Length);
|
|
result.UnlockBits(data);
|
|
return result;
|
|
}
|
|
|
|
public void RotateRight()
|
|
{
|
|
Bitmap result = new Bitmap(bitmap.Height, bitmap.Width);
|
|
|
|
for(int rowIndex=0;rowIndex<bitmap.Height;rowIndex++)
|
|
{
|
|
for(int colIndex=0;colIndex<bitmap.Width;colIndex++)
|
|
{
|
|
Color color=GetPixel(rowIndex,colIndex);
|
|
result.SetPixel((bitmap.Width-1)-colIndex,rowIndex,color);
|
|
}
|
|
}
|
|
|
|
bitmap.Dispose();
|
|
bitmap=result;
|
|
width=bitmap.Width;
|
|
height=bitmap.Height;
|
|
}
|
|
|
|
public void RotateLeft()
|
|
{
|
|
Bitmap result = new Bitmap(bitmap.Height, bitmap.Width);
|
|
|
|
for(int rowIndex=0;rowIndex<bitmap.Height;rowIndex++)
|
|
{
|
|
for(int colIndex=0;colIndex<bitmap.Width;colIndex++)
|
|
{
|
|
Color color=GetPixel(rowIndex,colIndex);
|
|
result.SetPixel(colIndex, (bitmap.Width-1)-rowIndex,color);
|
|
}
|
|
}
|
|
bitmap.Dispose();
|
|
bitmap=result;
|
|
width=bitmap.Width;
|
|
height=bitmap.Height;
|
|
}
|
|
|
|
public void ToGrayScale()
|
|
{
|
|
Validate();
|
|
Bitmap grayScaleBitmap = ToGrayScale(bitmap);
|
|
bitmap.Dispose();
|
|
bitmap=grayScaleBitmap;
|
|
width=bitmap.Width;
|
|
height=bitmap.Height;
|
|
}
|
|
|
|
private Bitmap ToGrayScale(Bitmap bmp)
|
|
{
|
|
Bitmap bitmap = new Bitmap(bmp.Width, bmp.Height);
|
|
|
|
for (int i = 0; i < bmp.Width; i++)
|
|
{
|
|
for (int x = 0; x < bmp.Height; x++)
|
|
{
|
|
Color oc = bmp.GetPixel(i, x);
|
|
int grayScale = (int)((oc.R * 0.3) + (oc.G * 0.59) + (oc.B * 0.11));
|
|
Color nc = Color.FromArgb(oc.A, grayScale, grayScale, grayScale);
|
|
bitmap.SetPixel(i, x, nc);
|
|
}
|
|
}
|
|
return bitmap;
|
|
}
|
|
|
|
public void Blur(Int32 blurSize)
|
|
{
|
|
Bitmap blurBmp=Blur(bitmap, new Rectangle(0, 0, bitmap.Width, bitmap.Height), blurSize);
|
|
bitmap.Dispose();
|
|
bitmap=blurBmp;
|
|
height=bitmap.Height;
|
|
width=bitmap.Width;
|
|
}
|
|
|
|
private static Bitmap Blur(Bitmap image, Rectangle rectangle, Int32 blurSize)
|
|
{
|
|
Bitmap blurred = new Bitmap(image.Width, image.Height);
|
|
|
|
// make an exact copy of the bitmap provided
|
|
using (Graphics graphics = Graphics.FromImage(blurred))
|
|
graphics.DrawImage(image, new Rectangle(0, 0, image.Width, image.Height), new Rectangle(0, 0, image.Width, image.Height), GraphicsUnit.Pixel);
|
|
|
|
// look at every pixel in the blur rectangle
|
|
for (int xx = rectangle.X; xx < rectangle.X + rectangle.Width; xx++)
|
|
{
|
|
for (int yy = rectangle.Y; yy < rectangle.Y + rectangle.Height; yy++)
|
|
{
|
|
int avgR = 0, avgG = 0, avgB = 0;
|
|
int blurPixelCount = 0;
|
|
|
|
// average the color of the red, green and blue for each pixel in the
|
|
// blur size while making sure you don't go outside the image bounds
|
|
for (int x = xx; (x < xx + blurSize && x < image.Width); x++)
|
|
{
|
|
for (int y = yy; (y < yy + blurSize && y < image.Height); y++)
|
|
{
|
|
Color pixel = blurred.GetPixel(x, y);
|
|
|
|
avgR += pixel.R;
|
|
avgG += pixel.G;
|
|
avgB += pixel.B;
|
|
|
|
blurPixelCount++;
|
|
}
|
|
}
|
|
|
|
avgR = avgR / blurPixelCount;
|
|
avgG = avgG / blurPixelCount;
|
|
avgB = avgB / blurPixelCount;
|
|
|
|
// now that we know the average for the blur size, set each pixel to that color
|
|
for (int x = xx; x < xx + blurSize && x < image.Width && x < rectangle.Width; x++)
|
|
{
|
|
for (int y = yy; y < yy + blurSize && y < image.Height && y < rectangle.Height; y++)
|
|
{
|
|
blurred.SetPixel(x, y, Color.FromArgb(avgR, avgG, avgB));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return blurred;
|
|
}
|
|
|
|
public Color GetPixel(int x,int y)
|
|
{
|
|
return bitmap.GetPixel(x,y);
|
|
}
|
|
public void SetPixel(int x, int y,Color color)
|
|
{
|
|
bitmap.SetPixel(x,y,color);
|
|
}
|
|
public Point TranslatePoint(Point point)
|
|
{
|
|
return pointMapping.TranslatePoint(point);
|
|
}
|
|
public void Validate()
|
|
{
|
|
if(null==bitmap)throw new InvalidDataException("The image has not been initialized");
|
|
}
|
|
public int Width
|
|
{
|
|
get{return width;}
|
|
}
|
|
public int Height
|
|
{
|
|
get{return height;}
|
|
}
|
|
public void Fill(Brush brush)
|
|
{
|
|
Validate();
|
|
Region region=new Region(new Rectangle(0,0,bitmap.Width,bitmap.Height));
|
|
graphics.FillRegion(brush,region);
|
|
}
|
|
|
|
public void DrawPoint(Pen pen,Point drawPoint)
|
|
{
|
|
Validate();
|
|
Point txPoint=pointMapping.MapPoint(drawPoint);
|
|
graphics.DrawImage(bitmap,txPoint);
|
|
}
|
|
public void AddNoise(Color color,double percentDecimal)
|
|
{
|
|
Random random=new Random();
|
|
int amount=(int)(percentDecimal*(Width*Height));
|
|
for(int index=0;index<amount;index++)
|
|
{
|
|
int row=random.Next(Height-1);
|
|
int col=random.Next(Width-1);
|
|
SetPixel(row,col,color);
|
|
}
|
|
}
|
|
public void DrawLine(Pen pen,Point srcPoint,Point dstPoint)
|
|
{
|
|
Validate();
|
|
Point txSrcPoint=pointMapping.MapPoint(srcPoint);
|
|
Point txDstPoint=pointMapping.MapPoint(dstPoint);
|
|
graphics.DrawLine(pen,txSrcPoint,txDstPoint);
|
|
}
|
|
public void DrawPath(Pen pen,LineSegments lineSegments)
|
|
{
|
|
GraphicsPath graphicsPath=new GraphicsPath();
|
|
foreach(LineSegment lineSegment in lineSegments)
|
|
{
|
|
graphicsPath.AddLine(pointMapping.MapPoint(lineSegment.P1),pointMapping.MapPoint(lineSegment.P2));
|
|
}
|
|
graphics.DrawPath(pen,graphicsPath);
|
|
}
|
|
public void DrawCircle(Brush brush,Point pt,float size=1.00f)
|
|
{
|
|
Validate();
|
|
Point txPoint=pointMapping.MapPoint(pt);
|
|
txPoint.X-=(int)(size/2f);
|
|
txPoint.Y-=(int)(size/2F);
|
|
Rectangle boundingRectangle=new Rectangle();
|
|
boundingRectangle.X=txPoint.X;
|
|
boundingRectangle.Y=txPoint.Y;
|
|
boundingRectangle.Width=(int)size;
|
|
boundingRectangle.Height=(int)size;
|
|
graphics.FillEllipse(brush,boundingRectangle);
|
|
}
|
|
}
|
|
}
|