tweaked autocrop

This commit is contained in:
John Andrews
2022-09-10 15:50:59 +12:00
parent 144f4d6bf5
commit e86d646339
2 changed files with 146 additions and 6 deletions

View File

@@ -1,4 +1,5 @@
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using System.ComponentModel;
@@ -27,16 +28,124 @@ public class AutoCropImage : ImageNode
if (threshold < 0)
threshold = 0.5f;
args.Logger?.ILog("Attempting to auto crop using threshold: " + threshold);
image.Mutate(c => c.EntropyCrop(threshold));
var scaleFactor = originalWidth > 4000 && originalHeight > 4000 ? 10 :
originalWidth > 2000 && originalHeight > 2000 ? 6 :
4;
if (image.Width == originalWidth && image.Height == originalHeight)
// entropycrop of Sixlabors does not give good results if the image has artifacts in it, eg from a scanned in image
// so we need to detect the whitespace ourselves. first we convert to greyscale and downscale, this should remove some artifacts for us
// when then look for the outer most non white pixels for out bounds
// then we upscale those bounds to the original dimensions and crop with those bounds
image.Mutate(c =>
{
c.Grayscale().Resize(originalWidth / scaleFactor, originalHeight / scaleFactor);
});
string temp = Path.Combine(args.TempPath, Guid.NewGuid() + ".jpg");
image.SaveAsJpeg(temp);
var bounds = GetTrimBounds(temp);
bounds.X *= scaleFactor;
bounds.Y *= scaleFactor;
bounds.Width *= scaleFactor;
bounds.Height *= scaleFactor;
image.Dispose();
args.Logger?.ILog("Attempting to auto crop using threshold: " + threshold);
if (bounds.Width == originalWidth && bounds.Height == originalHeight)
return 2;
using var image2 = Image.Load(args.WorkingFile, out format);
image2.Mutate(c =>
{
c.Crop(bounds);
//c.EntropyCrop(threshold);
});
if (image2.Width == originalWidth && image2.Height == originalHeight)
return 2;
var formatOpts = GetFormat(args);
SaveImage(args, image, formatOpts.file, formatOpts.format ?? format);
args.Logger?.ILog($"Image cropped from '{originalWidth}x{originalHeight}' to '{image.Width}x{image.Height}'");
SaveImage(args, image2, formatOpts.file, formatOpts.format ?? format);
args.Logger?.ILog($"Image cropped from '{originalWidth}x{originalHeight}' to '{image2.Width}x{image2.Height}'");
return 1;
}
public Rectangle GetTrimBounds(string file)
{
int threshhold = 255 - (this.Threshold / 4);
int topOffset = 0;
int bottomOffset = 0;
int leftOffset = 0;
int rightOffset = 0;
using Image<Rgba32> image = Image.Load<Rgba32>(file);
bool foundColor = false;
// Get left bounds to crop
for (int x = 1; x < image.Width && foundColor == false; x++)
{
for (int y = 1; y < image.Height && foundColor == false; y++)
{
var color = image[x, y];
if (color.R < threshhold || color.G < threshhold || color.B < threshhold)
foundColor = true;
}
leftOffset += 1;
}
foundColor = false;
// Get top bounds to crop
for (int y = 1; y < image.Height && foundColor == false; y++)
{
for (int x = 1; x < image.Width && foundColor == false; x++)
{
var color = image[x, y];
if (color.R < threshhold || color.G < threshhold || color.B < threshhold)
foundColor = true;
}
topOffset += 1;
}
foundColor = false;
// Get right bounds to crop
for (int x = image.Width - 1; x >= 1 && foundColor == false; x--)
{
for (int y = 1; y < image.Height && foundColor == false; y++)
{
var color = image[x, y];
if (color.R < threshhold || color.G < threshhold || color.B < threshhold)
foundColor = true;
}
rightOffset += 1;
}
foundColor = false;
// Get bottom bounds to crop
for (int y = image.Height - 1; y >= 1 && foundColor == false; y--)
{
for (int x = 1; x < image.Width && foundColor == false; x++)
{
var color = image[x, y];
if (color.R < threshhold || color.G < threshhold || color.B < threshhold)
foundColor = true;
}
bottomOffset += 1;
}
var bounds = new Rectangle(
leftOffset,
topOffset,
image.Width - leftOffset - rightOffset,
image.Height - topOffset - bottomOffset
);
return bounds;
}
}

View File

@@ -12,7 +12,7 @@ public class ImageNodesTests
string TestImage1;
string TestImage2;
string TempDir;
string TestCropImage1, TestCropImage2, TestCropImage3, TestCropImageNoCrop;
string TestCropImage1, TestCropImage2, TestCropImage3, TestCropImage4, TestCropImageNoCrop;
public ImageNodesTests()
{
@@ -25,6 +25,7 @@ public class ImageNodesTests
TestCropImage1 = @"D:\images\testimages\crop01.jpg";
TestCropImage2 = @"D:\images\testimages\crop02.jpg";
TestCropImage3 = @"D:\images\testimages\crop03.jpg";
TestCropImage4 = @"D:\images\testimages\crop04.jpg";
TestCropImageNoCrop = @"D:\images\testimages\nocrop.jpg";
}
else
@@ -126,6 +127,8 @@ public class ImageNodesTests
};
var node = new AutoCropImage();
node.Threshold = 50;
node.PreExecute(args);
int result = node.Execute(args);
string log = logger.ToString();
@@ -159,6 +162,32 @@ public class ImageNodesTests
};
var node = new AutoCropImage();
node.Threshold = 50;
node.PreExecute(args);
int result = node.Execute(args);
string log = logger.ToString();
Assert.AreEqual(1, result);
}
[TestMethod]
public void ImageNodes_Basic_AutoCrop_04()
{
var logger = new TestLogger();
var args = new NodeParameters(TestCropImage4, logger, false, string.Empty)
{
TempPath = TempDir
};
//var rotate = new ImageRotate();
//rotate.Angle = 270;
//rotate.PreExecute(args);
//rotate.Execute(args);
var node = new AutoCropImage();
node.Threshold = 70;
node.PreExecute(args);
int result = node.Execute(args);
string log = logger.ToString();
@@ -175,6 +204,8 @@ public class ImageNodesTests
};
var node = new AutoCropImage();
node.Threshold = 50;
node.PreExecute(args);
int result = node.Execute(args);
string log = logger.ToString();