forked from Azure/usql
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added more examples for Image app sample
Includes a Processor and a feature extractor now. Also added more comments to scripts and moved the constants into the Assembly.
- Loading branch information
Showing
16 changed files
with
697 additions
and
35 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
} | ||
} | ||
} | ||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
} | ||
} | ||
} | ||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
|
||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
} | ||
} | ||
} | ||
|
||
|
Oops, something went wrong.