Skip to content

Commit

Permalink
Added more examples for Image app sample
Browse files Browse the repository at this point in the history
Includes a Processor and a feature extractor now. Also added more
comments to scripts and moved the constants into the Assembly.
  • Loading branch information
MikeRys committed Sep 27, 2016
1 parent 14d9314 commit 46403f6
Show file tree
Hide file tree
Showing 16 changed files with 697 additions and 35 deletions.
27 changes: 27 additions & 0 deletions Examples/ImageApp/Image/ImageExtractor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using Microsoft.Analytics.Interfaces;
using Microsoft.Analytics.Types.Sql;
using System;
using System.Collections.Generic;
using System.Linq;

using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;

namespace Images
{
[SqlUserDefinedExtractor(AtomicFileProcessing = true)]
public class ImageExtractor : IExtractor
{
public override IEnumerable<IRow> Extract(IUnstructuredReader input, IUpdatableRow output)
{
byte[] imageArray = ImageOps.GetByteArrayforImage(input.BaseStream);
output.Set<byte[]>(0, imageArray);
yield return output.AsReadOnly();
}
}
}



43 changes: 43 additions & 0 deletions Examples/ImageApp/Image/ImageFeatureExtractor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using Microsoft.Analytics.Interfaces;
using Microsoft.Analytics.Types.Sql;
using System;
using System.Collections.Generic;
using System.Linq;

using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;

namespace Images
{
[SqlUserDefinedExtractor(AtomicFileProcessing = true)]
public class ImageFeatureExtractor : IExtractor
{
private int _scaleWidth, _scaleHeight;

public ImageFeatureExtractor(int scaleWidth = 150, int scaleHeight = 150)
{ _scaleWidth = scaleWidth; _scaleHeight = scaleHeight; }

public override IEnumerable<IRow> Extract(IUnstructuredReader input, IUpdatableRow output)
{
byte[] img = ImageOps.GetByteArrayforImage(input.BaseStream);
if (output.Schema.IndexOf("image") != -1)
output.Set<byte[]>("image", img);
if (output.Schema.IndexOf("equipment_make") != -1)
output.Set<string>("equipment_make", Images.ImageOps.getImageProperty(img, ImageProperties.equipment_make));
if (output.Schema.IndexOf("equipment_model") != -1)
output.Set<string>("equipment_model", Images.ImageOps.getImageProperty(img, ImageProperties.equipment_model));
if (output.Schema.IndexOf("description") != -1)
output.Set<string>("description", Images.ImageOps.getImageProperty(img, ImageProperties.description));
if (output.Schema.IndexOf("copyright") != -1)
output.Set<string>("copyright", Images.ImageOps.getImageProperty(img, ImageProperties.copyright));
if (output.Schema.IndexOf("thumbnail") != -1)
output.Set<byte[]>("thumbnail", Images.ImageOps.scaleImageTo(img, this._scaleWidth, this._scaleHeight));
yield return output.AsReadOnly();
}
}
}



256 changes: 256 additions & 0 deletions Examples/ImageApp/Image/ImageOps.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
using Microsoft.Analytics.Interfaces;
using Microsoft.Analytics.Types.Sql;
using System;
using System.Collections.Generic;
using System.Linq;

using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;

namespace Images
{
// Helper class that binds together a stream and an image so that they can be disposed together.
// It is needed because the stream must stay open for as long as the image is being used.
class StreamImage : IDisposable
{
private MemoryStream mMemoryStream;
public Image mImage;

public StreamImage(byte[] inBytes)
{
mMemoryStream = new MemoryStream(inBytes);
mImage = null;
try
{
mImage = Image.FromStream(mMemoryStream);
}
finally
{
if (mImage == null)
{
mMemoryStream.Dispose();
}
}
}

public void Dispose()
{
try
{
mImage.Dispose();
}
finally
{
mMemoryStream.Dispose();
}
}
}

// Sample image utilities and operations.
public class ImageOps
{
// The quality setting for generated JPEG files.
private const long JPEG_QUALITY = 90;

// Utility: convert a byte array into an image.
private static StreamImage byteArrayToImage(byte[] inBytes)
{
return new StreamImage(inBytes);
}

// Utility: convert an image into a byte array containing a JPEG encoding of the image.
private static byte[] imageToByteArray(Image inImage)
{
using (MemoryStream outStream = new MemoryStream())
{
ImageCodecInfo jpegCodec = null;
foreach (ImageCodecInfo codec in ImageCodecInfo.GetImageEncoders())
{
if (codec.FormatID == ImageFormat.Jpeg.Guid)
{
jpegCodec = codec;
break;
}
}
if (jpegCodec == null)
{
throw new MissingMemberException("Cannot find JPEG encoder");
}
using (EncoderParameters ep = new EncoderParameters(1))
{
using (EncoderParameter p = new EncoderParameter(Encoder.Quality, JPEG_QUALITY))
{
ep.Param[0] = p;
inImage.Save(outStream, jpegCodec, ep);
return outStream.ToArray();
}
}
}
}

// Utility: draw the input image to the output image within the given region (at high quality).
private static void drawImage(Image inImage, Bitmap outImage, Rectangle region)
{
outImage.SetResolution(inImage.HorizontalResolution, inImage.VerticalResolution);
using (Graphics g = Graphics.FromImage(outImage))
{
// Clear background pixels, if any will remain after the drawing below.
if ((region.X != 0) ||
(region.Y != 0) ||
(region.Height != outImage.Height) ||
(region.Width != outImage.Width))
{
g.Clear(Color.Black);
}
// Draw image at high quality.
g.CompositingMode = CompositingMode.SourceCopy;
g.CompositingQuality = CompositingQuality.HighQuality;
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.SmoothingMode = SmoothingMode.HighQuality;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
using (ImageAttributes attributes = new ImageAttributes())
{
attributes.SetWrapMode(WrapMode.TileFlipXY);
g.DrawImage(inImage, region, 0, 0, inImage.Width, inImage.Height, GraphicsUnit.Pixel, attributes);
}
}
}

// Operation: return the value of an image property (provided it's a string or integer).
// Use property ID 8298 for copyright.
// See https://msdn.microsoft.com/en-us/library/ms534416.aspx for additional IDs.
public static string getImageProperty(byte[] inBytes, int propertyId)
{
using (StreamImage inImage = byteArrayToImage(inBytes))
{
foreach (PropertyItem propItem in inImage.mImage.PropertyItems)
{
if (propItem.Id == propertyId)
{
return (propItem.Type == 2) ? System.Text.Encoding.UTF8.GetString(propItem.Value) : propItem.Value.ToString();
}
}
}
return null;
}


// Operation: rotate an image by 90, 180, or 270 degrees.
public static byte[] rotateImage(byte[] inBytes, int rotateType)
{
RotateFlipType rotateFlipType;
switch (rotateType)
{
case 1:
rotateFlipType = RotateFlipType.Rotate90FlipNone;
break;
case 2:
rotateFlipType = RotateFlipType.Rotate180FlipNone;
break;
case 3:
rotateFlipType = RotateFlipType.Rotate270FlipNone;
break;
default:
throw new ArgumentException("Invalid type");
}
using (StreamImage inImage = byteArrayToImage(inBytes))
{
inImage.mImage.RotateFlip(rotateFlipType);
return imageToByteArray(inImage.mImage);
}
}

// Operation: scale an image by a scale factor.
public static byte[] scaleImageBy(byte[] inBytes, float scaleFactor)
{
using (StreamImage inImage = byteArrayToImage(inBytes))
{
int outWidth = (int)(inImage.mImage.Width * scaleFactor);
int outHeight = (int)(inImage.mImage.Height * scaleFactor);
using (Bitmap outImage = new Bitmap(outWidth, outHeight))
{
drawImage(inImage.mImage, outImage, new Rectangle(0, 0, outWidth, outHeight));
return imageToByteArray(outImage);
}
}
}

// Operation: scale an image to the given dimensions.
public static byte[] scaleImageTo(byte[] inBytes, int outWidth, int outHeight)
{
using (StreamImage inImage = byteArrayToImage(inBytes))
{
int x, y, w, h;
int iWoH = inImage.mImage.Width * outHeight;
int iHoW = inImage.mImage.Height * outWidth;
if (iWoH < iHoW)
{
w = (int)((double)(iWoH) / inImage.mImage.Height);
h = outHeight;
x = (int)((outWidth - w) / 2.0);
y = 0;
}
else
{
w = outWidth;
h = (int)((double)(iHoW) / inImage.mImage.Width);
x = 0;
y = (int)((outHeight - h) / 2.0);
}
using (Bitmap outImage = new Bitmap(outWidth, outHeight))
{
drawImage(inImage.mImage, outImage, new Rectangle(x, y, w, h));
return imageToByteArray(outImage);
}
}
}


public static byte[] GetByteArrayforImage(Stream input)
{
try
{
var image = Image.FromStream(input);
MemoryStream ms = new MemoryStream();
image.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
return ms.ToArray();
}
catch (Exception)
{
return null;
}
}

public static string[] ConvertTiffToJpeg(string fileName)
{
using (Image imageFile = Image.FromFile(fileName))
{
FrameDimension frameDimensions = new FrameDimension(
imageFile.FrameDimensionsList[0]);

// Gets the number of pages from the tiff image (if multipage)
int frameNum = imageFile.GetFrameCount(frameDimensions);
string[] jpegPaths = new string[frameNum];

for (int frame = 0; frame < frameNum; frame++)
{
// Selects one frame at a time and save as jpeg.
imageFile.SelectActiveFrame(frameDimensions, frame);
using (Bitmap bmp = new Bitmap(imageFile))
{
jpegPaths[frame] = String.Format("{0}\\{1}{2}.jpg",
Path.GetDirectoryName(fileName),
Path.GetFileNameWithoutExtension(fileName),
frame);
bmp.Save(jpegPaths[frame], ImageFormat.Jpeg);
}
}

return jpegPaths;
}

}
}
}
29 changes: 29 additions & 0 deletions Examples/ImageApp/Image/ImageOutputter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using Microsoft.Analytics.Interfaces;
using Microsoft.Analytics.Types.Sql;
using System;
using System.Collections.Generic;
using System.Linq;

using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;

namespace Images
{
[SqlUserDefinedExtractor(AtomicFileProcessing = true)]
public class ImageOutputter : IOutputter
{
public override void Output(IRow input, IUnstructuredWriter output)
{
var obj = input.Get<object>(0);
byte[] imageArray = (byte[])obj;
using (MemoryStream ms = new MemoryStream(imageArray))
{
var image = Image.FromStream(ms);
image.Save(output.BaseStream, ImageFormat.Jpeg);
}
}
}
}

31 changes: 31 additions & 0 deletions Examples/ImageApp/Image/ImageProcessor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using Microsoft.Analytics.Interfaces;
using Microsoft.Analytics.Types.Sql;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Images
{
public class ImageProcessor : IProcessor
{
public override IRow Process(IRow input, IUpdatableRow output)
{
var img = input.Get<byte[]>("image_data");

if (output.Schema.IndexOf("equipment_make") != -1)
output.Set<string>("equipment_make", Images.ImageOps.getImageProperty(img, ImageProperties.equipment_make));
if (output.Schema.IndexOf("equipment_model") != -1)
output.Set<string>("equipment_model", Images.ImageOps.getImageProperty(img, ImageProperties.equipment_model));
if (output.Schema.IndexOf("description") != -1)
output.Set<string>("description", Images.ImageOps.getImageProperty(img, ImageProperties.description));
if (output.Schema.IndexOf("copyright") != -1)
output.Set<string>("copyright", Images.ImageOps.getImageProperty(img, ImageProperties.copyright));
if (output.Schema.IndexOf("thumbnail") != -1)
output.Set<byte[]>("thumbnail", Images.ImageOps.scaleImageTo(img, 150, 150));
return output.AsReadOnly();
}
}
}


Loading

0 comments on commit 46403f6

Please sign in to comment.