Skip to content

Commit

Permalink
Support TIF and HEIC #17 #84
Browse files Browse the repository at this point in the history
  • Loading branch information
Webreaper committed Aug 9, 2021
1 parent 35d1b7a commit 9ed9b56
Show file tree
Hide file tree
Showing 11 changed files with 170 additions and 64 deletions.
Binary file modified .DS_Store
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,18 @@

namespace Damselfly.Core.ImageProcessing
{
public class GraphicsMagicProcessor : IImageProcessor
public class ImageMagickProcessor : IImageProcessor
{
// TODO: Add check that GM exists

// SkiaSharp doesn't handle .heic files... yet
private static readonly string[] s_imageExtensions = { ".jpg", ".jpeg", ".png", /*".heic", */".webp" };
private static readonly string[] s_imageExtensions = { ".jpg", ".jpeg", ".png", ".heic", ".tif", ".tiff", ".webp" };

public ICollection<string> SupportedFileExtensions { get { return s_imageExtensions; } }
public static ICollection<string> SupportedFileExtensions { get { return s_imageExtensions; } }

const string imageMagickExe = "convert";
const string graphicsMagickExe = "gm";
private bool s_useGraphicsMagick = true;
private bool s_useGraphicsMagick = false; // GM doesn't support HEIC yet.

public GraphicsMagicProcessor()
public ImageMagickProcessor()
{
CheckToolStatus();
}
Expand All @@ -33,28 +31,16 @@ public GraphicsMagicProcessor()
/// </summary>
private void CheckToolStatus()
{
ProcessStarter gmprocess = new ProcessStarter();
bool gmAvailable = gmprocess.StartProcess("gm", "-version");
ProcessStarter improcess = new ProcessStarter();
bool imAvailable = improcess.StartProcess("convert", "--version");

if (!gmAvailable)
if (imAvailable)
{
ProcessStarter improcess = new ProcessStarter();
bool imAvailable = improcess.StartProcess("convert", "--version");

if (imAvailable)
{
var verString = gmprocess.OutputText?.Split('\n').FirstOrDefault();
Logging.Log($"ImageMagick found: {verString}");
}
else
throw new ApplicationException("Neither ImageMagick or GraphicsMagick were found.");
var verString = improcess.OutputText?.Split('\n').FirstOrDefault();
Logging.Log($"ImageMagick found: {verString}");
}
else
{
s_useGraphicsMagick = true;
var verString = gmprocess.OutputText?.Split('\n').FirstOrDefault();
Logging.Log($"GraphicsMagick found: {verString}");
}
Logging.LogError("ImageMagick not found.");
}

/// <summary>
Expand Down
63 changes: 63 additions & 0 deletions Damselfly.Core/ImageProcessing/ImageProcessorFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Damselfly.Core.Interfaces;

namespace Damselfly.Core.ImageProcessing
{
public class ImageProcessorFactory
{
private ImageMagickProcessor imProcessor;
private SkiaSharpProcessor skiaProcessor;
private ImageSharpProcessor isharpProcessor;
private string rootContentPath;

public void SetContentPath(string path)
{
rootContentPath = path;
}

/// <summary>
/// Takes a file extension, and returns an ImageProcessor that can generate
/// thumbnails for that file type.
/// </summary>
/// <param name="fileExtension"></param>
/// <returns></returns>
public IImageProcessor GetProcessor( string fileExtension )
{
if( ! fileExtension.StartsWith( "." ) )
{
fileExtension = $".{fileExtension}";
}

if( SkiaSharpProcessor.SupportedFileExtensions.Any( x => x.Equals( fileExtension, StringComparison.OrdinalIgnoreCase ) ) )
{
if (skiaProcessor == null)
skiaProcessor = new SkiaSharpProcessor();

return skiaProcessor;
}

if (ImageSharpProcessor.SupportedFileExtensions.Any(x => x.Equals(fileExtension, StringComparison.OrdinalIgnoreCase)))
{
if (isharpProcessor == null)
{
isharpProcessor = new ImageSharpProcessor();
isharpProcessor.SetFontPath(Path.Combine(rootContentPath, "fonts"));
}
return isharpProcessor;
}

if (ImageMagickProcessor.SupportedFileExtensions.Any(x => x.Equals(fileExtension, StringComparison.OrdinalIgnoreCase)))
{
if (imProcessor == null)
imProcessor = new ImageMagickProcessor();

return imProcessor;
}

return null;
}
}
}
4 changes: 2 additions & 2 deletions Damselfly.Core/ImageProcessing/ImageSharpProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ namespace Damselfly.Core.ImageProcessing
public class ImageSharpProcessor : IImageProcessor
{
private static FontCollection fontCollection;
private static readonly string[] s_imageExtensions = { ".jpg", ".jpeg", ".png", ".webp" };
private static readonly string[] s_imageExtensions = { ".jpg", ".jpeg", ".png", ".webp", ".tga", ".gif", ".bmp" };

public ICollection<string> SupportedFileExtensions { get { return s_imageExtensions; } }
public static ICollection<string> SupportedFileExtensions { get { return s_imageExtensions; } }

public ImageSharpProcessor()
{
Expand Down
8 changes: 4 additions & 4 deletions Damselfly.Core/ImageProcessing/SkiaSharpProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public class SkiaSharpProcessor : IImageProcessor
// SkiaSharp doesn't handle .heic files... yet
private static readonly string[] s_imageExtensions = { ".jpg", ".jpeg", ".png", /*".heic", */".webp", ".bmp", ".dng", ".cr2" };

public ICollection<string> SupportedFileExtensions { get { return s_imageExtensions; } }
public static ICollection<string> SupportedFileExtensions { get { return s_imageExtensions; } }

/// <summary>
/// Create an SHA1 hash from the image data (pixels only) to allow us to find
Expand Down Expand Up @@ -70,11 +70,11 @@ public Task<ImageProcessResult> CreateThumbs(FileInfo source, IDictionary<FileIn

try
{
thumbs = new Stopwatch("SkiaSharpThumbs");
thumbs = new Stopwatch("GenThumbs");

int desiredWidth = destFiles.Max(x => x.Value.width);

load = new Stopwatch("LoadTHumb");
load = new Stopwatch("LoadThumb");
using var sourceBitmap = LoadOrientedBitmap(source, desiredWidth);
load.Stop();

Expand Down Expand Up @@ -132,7 +132,7 @@ public Task<ImageProcessResult> CreateThumbs(FileInfo source, IDictionary<FileIn
}
catch (Exception ex)
{
Logging.Log($"Exception during Skia processing: {ex.Message}");
Logging.Log($"Exception during Thumbnail processing: {ex.Message}");
throw;
}

Expand Down
2 changes: 1 addition & 1 deletion Damselfly.Core/Interfaces/IImageProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,6 @@ public interface IImageProcessor
{
Task<ImageProcessResult> CreateThumbs(FileInfo source, IDictionary<FileInfo, ThumbConfig> destFiles );
void TransformDownloadImage(string input, Stream output, ExportConfig config);
ICollection<string> SupportedFileExtensions { get; }
static ICollection<string> SupportedFileExtensions { get; }
}
}
5 changes: 3 additions & 2 deletions Damselfly.Core/Services/ConfigService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ namespace Damselfly.Core.Services
/// </summary>
public class ConfigService : IConfigService
{
private IDictionary<string, ConfigSetting> _cache;
private readonly IDictionary<string, ConfigSetting> _cache = new Dictionary<string, ConfigSetting>(StringComparer.OrdinalIgnoreCase);

public ConfigService()
{
Expand All @@ -23,7 +23,8 @@ public void InitialiseCache( bool force = false )
if (_cache == null || force)
{
using var db = new ImageContext();
_cache = db.ConfigSettings.ToDictionary(x => x.Name, x => x, StringComparer.OrdinalIgnoreCase);
foreach (var setting in db.ConfigSettings )
_cache[setting.Name] = setting;
}
}

Expand Down
51 changes: 38 additions & 13 deletions Damselfly.Core/Services/ImageProcessService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,40 +23,65 @@ namespace Damselfly.Core.Services
/// </summary>
public class ImageProcessService : IImageProcessor
{
private readonly IImageProcessor _processor;
private readonly ImageProcessorFactory _factory;

public ImageProcessService(IImageProcessor processor)
public ImageProcessService()
{
_processor = processor;

Logging.Log($"Initialised {processor.GetType().Name} for thumbnail processing.");
_factory = new ImageProcessorFactory();
}

public void SetContentPath( string path )
{
if( _processor is ImageSharpProcessor imageSharp )
imageSharp.SetFontPath(Path.Combine(path, "fonts"));
_factory.SetContentPath(path);
}

/// <summary>
/// Creates a set of thumbs for an input image
/// </summary>
/// <param name="source"></param>
/// <param name="destFiles"></param>
/// <returns></returns>
public async Task<ImageProcessResult> CreateThumbs(FileInfo source, IDictionary<FileInfo, ThumbConfig> destFiles)
{
return await _processor.CreateThumbs(source, destFiles);
var processor = _factory.GetProcessor(source.Extension);

if( processor != null )
return await processor.CreateThumbs(source, destFiles);

return new ImageProcessResult { ThumbsGenerated = false };
}

/// <summary>
/// Convert an image, optionally watermarking.
/// </summary>
/// <param name="input"></param>
/// <param name="output"></param>
/// <param name="config"></param>
public void TransformDownloadImage(string input, Stream output, ExportConfig config)
{
_processor.TransformDownloadImage(input, output, config);
var ext = Path.GetExtension(input);

var processor = _factory.GetProcessor(ext);

if (processor != null)
processor.TransformDownloadImage(input, output, config);
}

/// <summary>
/// Returns true if the file is one that we consider to be an image - that is,
/// one that we have an image processor for, which will generate thumbs, etc.
/// </summary>
/// <param name="filename"></param>
/// <returns></returns>
public bool IsImageFileType(FileInfo filename)
{
if (filename.IsHidden())
return false;

return _processor.SupportedFileExtensions.Any(x => x.Equals(filename.Extension, StringComparison.OrdinalIgnoreCase));
}

var processor = _factory.GetProcessor(filename.Extension);

public ICollection<string> SupportedFileExtensions { get{ return _processor.SupportedFileExtensions; } }
// If we have a valid processor, we're good.
return processor != null;
}
}
}
59 changes: 45 additions & 14 deletions Damselfly.Core/Services/ImageRecognitionService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -154,25 +154,49 @@ private bool UseAzureForRecogition(IList<ImageDetectResult> objects)
return false;
}

private System.Drawing.Bitmap SafeLoadBitmap(string fileName)
{
System.Drawing.Bitmap bmp = null;
try
{
// Load the bitmap once
bmp = new System.Drawing.Bitmap(fileName);
}
catch( Exception ex )
{
Logging.LogError($"Error loading bitmap for {fileName}: {ex}");
}

return bmp;
}
/// <summary>
/// Detect objects in the image.
/// </summary>
/// <param name="image"></param>
/// <returns></returns>
private async Task DetectObjects(Image image)
{
var file = new FileInfo(image.FullPath);
var thumbSize = ThumbSize.Large;
var medThumb = new FileInfo(_thumbService.GetThumbPath(file, thumbSize));
var fileName = Path.Combine(image.Folder.Path, image.FileName);

try
{
var foundObjects = new List<ImageObject>();
var foundFaces = new List<ImageObject>();
var file = new FileInfo(image.FullPath);
var thumbSize = ThumbSize.Large;
var medThumb = new FileInfo(_thumbService.GetThumbPath(file, thumbSize));
var fileName = Path.Combine(image.Folder.Path, image.FileName);
Logging.Log($"Processing AI image detection for {file.Name}...");

// Load the bitmap once
var bitmap = new System.Drawing.Bitmap(medThumb.FullName);
if (!File.Exists(medThumb.FullName) )
{
// The thumb isn't ready yet.
return;
}

var bitmap = SafeLoadBitmap(medThumb.FullName);

if (bitmap == null)
return;

// Next, look for faces. We need to determine if we:
// a) Use only local (Accord.Net) detection
Expand Down Expand Up @@ -351,7 +375,7 @@ private async Task DetectObjects(Image image)
var allFound = foundObjects.Union(foundFaces).ToList();

// Write faces locally with rectangles - for debugging
DrawRects(image.FullPath, allFound);
DrawRects(medThumb.FullName, allFound);

using var db = new ImageContext();

Expand All @@ -365,7 +389,7 @@ private async Task DetectObjects(Image image)
}
catch (Exception ex)
{
Logging.LogError($"Exception during AI detection: {ex}");
Logging.LogError($"Exception during AI detection for {fileName}: {ex}");
}
}

Expand Down Expand Up @@ -408,14 +432,21 @@ private void DrawRects(string fullPath, List<ImageObject> imgObjs)
{
if (System.Diagnostics.Debugger.IsAttached)
{
string outDir = "/Users/markotway/Desktop/Faces";
if (!System.IO.Directory.Exists(outDir))
System.IO.Directory.CreateDirectory(outDir);
try
{
string outDir = "/Users/markotway/Desktop/Faces";
if (!System.IO.Directory.Exists(outDir))
System.IO.Directory.CreateDirectory(outDir);

var output = Path.Combine(outDir, Path.GetFileName(fullPath));
var output = Path.Combine(outDir, Path.GetFileName(fullPath));

var rects = imgObjs.Select(x => new SixLabors.ImageSharp.Rectangle(x.RectX, x.RectY, x.RectWidth, x.RectHeight)).ToList();
ImageSharpProcessor.DrawRects(fullPath, rects, output);
var rects = imgObjs.Select(x => new SixLabors.ImageSharp.Rectangle(x.RectX, x.RectY, x.RectWidth, x.RectHeight)).ToList();
ImageSharpProcessor.DrawRects(fullPath, rects, output);
}
catch (Exception ex)
{
Logging.LogError($"Exception while drawing rects for {fullPath}: {ex}");
}
}
}

Expand Down
4 changes: 2 additions & 2 deletions Damselfly.Core/Services/ThumbnailService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ public string GetThumbPath(FileInfo imageFile, ThumbSize size)
}
else
{
string extension = Path.GetExtension(imageFile.Name);
string extension = ".jpg";// Always use .jpg - we don't want to create .heic thumbs, etc
string baseName = Path.GetFileNameWithoutExtension(imageFile.Name);
string relativePath = imageFile.DirectoryName.MakePathRelativeTo(PicturesRoot);
string thumbFileName = $"{baseName}_{GetSizePostFix(size)}{extension}";
Expand Down Expand Up @@ -169,7 +169,7 @@ private Dictionary<FileInfo, ThumbConfig> GetThumbConfigs(FileInfo source, bool
{
var destFile = new FileInfo( GetThumbPath(source, thumbConfig.size) );

if( ! destFile.Directory.Exists )
if ( ! destFile.Directory.Exists )
{
Logging.LogTrace("Creating directory: {0}", destFile.Directory.FullName);
var newDir = System.IO.Directory.CreateDirectory( destFile.Directory.FullName );
Expand Down
Loading

0 comments on commit 9ed9b56

Please sign in to comment.