naps2/NAPS2.Sdk/Images/BlankDetector.cs

65 lines
2.6 KiB
C#
Raw Normal View History

using System.Runtime.InteropServices;
using NAPS2.Images.Gdi;
2018-12-04 04:12:20 +03:00
2021-12-17 22:59:54 +03:00
namespace NAPS2.Images;
public static class BlankDetector
2018-12-04 04:12:20 +03:00
{
2021-12-17 22:59:54 +03:00
// If the pixel value (0-255) >= white_threshold, then it counts as a white pixel.
private const int WHITE_THRESHOLD_MIN = 1;
private const int WHITE_THRESHOLD_MAX = 255;
// If the fraction of non-white pixels > coverage_threshold, then it counts as a non-blank page.
private const double COVERAGE_THRESHOLD_MIN = 0.00;
private const double COVERAGE_THRESHOLD_MAX = 0.01;
2018-12-04 04:12:20 +03:00
public static bool IsBlank(IMemoryImage image, int whiteThresholdNorm, int coverageThresholdNorm)
2021-12-17 22:59:54 +03:00
{
if (image.PixelFormat == ImagePixelFormat.BW1)
2018-12-04 04:12:20 +03:00
{
2021-12-17 22:59:54 +03:00
// TODO: Make more generic
if (!(image is GdiImage gdiImage))
{
2021-12-17 22:59:54 +03:00
throw new InvalidOperationException("Patch code detection only supported for GdiStorage");
}
2021-12-17 22:59:54 +03:00
using var bitmap2 = BitmapHelper.CopyToBpp(gdiImage.Bitmap, 8);
return IsBlankRGB(new GdiImage(bitmap2), whiteThresholdNorm, coverageThresholdNorm);
2018-12-04 04:12:20 +03:00
}
if (image.PixelFormat != ImagePixelFormat.RGB24)
{
2021-12-17 22:59:54 +03:00
return false;
}
return IsBlankRGB(image, whiteThresholdNorm, coverageThresholdNorm);
}
private static bool IsBlankRGB(IMemoryImage image, int whiteThresholdNorm, int coverageThresholdNorm)
2021-12-17 22:59:54 +03:00
{
var whiteThreshold = (int)Math.Round(WHITE_THRESHOLD_MIN + (whiteThresholdNorm / 100.0) * (WHITE_THRESHOLD_MAX - WHITE_THRESHOLD_MIN));
var coverageThreshold = COVERAGE_THRESHOLD_MIN + (coverageThresholdNorm / 100.0) * (COVERAGE_THRESHOLD_MAX - COVERAGE_THRESHOLD_MIN);
2021-12-17 22:59:54 +03:00
long totalPixels = image.Width * image.Height;
long matchPixels = 0;
2018-12-04 04:12:20 +03:00
using var data = image.Lock(LockMode.ReadOnly, out var scan0, out var stride);
2021-12-17 22:59:54 +03:00
var bytes = new byte[stride * image.Height];
Marshal.Copy(scan0, bytes, 0, bytes.Length);
data.Dispose();
2021-12-17 22:59:54 +03:00
for (int x = 0; x < image.Width; x++)
{
for (int y = 0; y < image.Height; y++)
{
2021-12-17 22:59:54 +03:00
int r = bytes[stride * y + x * 3];
int g = bytes[stride * y + x * 3 + 1];
int b = bytes[stride * y + x * 3 + 2];
// Use standard values for grayscale conversion to weight the RGB values
int luma = r * 299 + g * 587 + b * 114;
if (luma < whiteThreshold * 1000)
{
2021-12-17 22:59:54 +03:00
matchPixels++;
}
}
}
2021-12-17 22:59:54 +03:00
var coverage = matchPixels / (double)totalPixels;
return coverage < coverageThreshold;
2018-12-04 04:12:20 +03:00
}
2021-12-17 22:59:54 +03:00
}