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 /// 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. /// 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); } /// /// Resize image to given width and height. The documenation on CNN's indicates that resizing will work better than maintaining apsect /// ration with padding /// 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