173 lines
5.6 KiB
C#
173 lines
5.6 KiB
C#
using System;
|
|
using System.Configuration;
|
|
using System.Drawing;
|
|
using System.Drawing.Drawing2D;
|
|
using System.Drawing.Imaging;
|
|
using System.IO;
|
|
|
|
namespace HanvonF710XAttendanceService
|
|
{
|
|
internal static class NedoPhotoPreprocessor
|
|
{
|
|
public const int DefaultMinShortSide = 480;
|
|
public const int HanvonMaxSide = 1024;
|
|
public const int HanvonMaxBytes = 500 * 1024;
|
|
|
|
public static bool TryPrepareForHanvon(string photoBase64, out string preparedBase64, out int width, out int height, out string note)
|
|
{
|
|
preparedBase64 = null;
|
|
width = 0;
|
|
height = 0;
|
|
note = null;
|
|
|
|
if (string.IsNullOrWhiteSpace(photoBase64))
|
|
{
|
|
note = "empty";
|
|
return false;
|
|
}
|
|
|
|
byte[] sourceBytes;
|
|
try
|
|
{
|
|
sourceBytes = Convert.FromBase64String(photoBase64);
|
|
}
|
|
catch
|
|
{
|
|
note = "invalid_base64";
|
|
return false;
|
|
}
|
|
|
|
if (!IsCompleteJpeg(sourceBytes))
|
|
{
|
|
note = "incomplete_jpeg";
|
|
return false;
|
|
}
|
|
|
|
int minShortSide = GetIntSetting("NEDO_FACE_MIN_SHORT_SIDE", DefaultMinShortSide);
|
|
byte[] outputBytes;
|
|
using (var inputStream = new MemoryStream(sourceBytes))
|
|
using (var image = Image.FromStream(inputStream))
|
|
{
|
|
width = image.Width;
|
|
height = image.Height;
|
|
int shortSide = Math.Min(width, height);
|
|
if (shortSide >= minShortSide && sourceBytes.Length <= HanvonMaxBytes)
|
|
{
|
|
preparedBase64 = photoBase64;
|
|
note = "unchanged";
|
|
return true;
|
|
}
|
|
|
|
double scale = 1.0;
|
|
if (shortSide < minShortSide)
|
|
{
|
|
scale = (double)minShortSide / shortSide;
|
|
}
|
|
|
|
int targetWidth = (int)Math.Round(width * scale);
|
|
int targetHeight = (int)Math.Round(height * scale);
|
|
int longSide = Math.Max(targetWidth, targetHeight);
|
|
if (longSide > HanvonMaxSide)
|
|
{
|
|
double fit = (double)HanvonMaxSide / longSide;
|
|
targetWidth = (int)Math.Round(targetWidth * fit);
|
|
targetHeight = (int)Math.Round(targetHeight * fit);
|
|
}
|
|
|
|
using (var bitmap = new Bitmap(targetWidth, targetHeight))
|
|
using (var graphics = Graphics.FromImage(bitmap))
|
|
{
|
|
graphics.CompositingQuality = CompositingQuality.HighQuality;
|
|
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
|
|
graphics.SmoothingMode = SmoothingMode.HighQuality;
|
|
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
|
|
graphics.DrawImage(image, 0, 0, targetWidth, targetHeight);
|
|
|
|
using (var outputStream = new MemoryStream())
|
|
{
|
|
var encoder = GetJpegEncoder();
|
|
if (encoder != null)
|
|
{
|
|
using (var parameters = new EncoderParameters(1))
|
|
{
|
|
parameters.Param[0] = new EncoderParameter(Encoder.Quality, 92L);
|
|
bitmap.Save(outputStream, encoder, parameters);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bitmap.Save(outputStream, ImageFormat.Jpeg);
|
|
}
|
|
|
|
outputBytes = outputStream.ToArray();
|
|
}
|
|
}
|
|
|
|
width = targetWidth;
|
|
height = targetHeight;
|
|
}
|
|
|
|
if (outputBytes.Length > HanvonMaxBytes)
|
|
{
|
|
note = "too_large_after_prepare";
|
|
return false;
|
|
}
|
|
|
|
preparedBase64 = Convert.ToBase64String(outputBytes);
|
|
note = "upscaled";
|
|
return true;
|
|
}
|
|
|
|
public static bool IsCompleteJpeg(byte[] bytes)
|
|
{
|
|
if (bytes == null || bytes.Length < 4)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (bytes[0] != 0xFF || bytes[1] != 0xD8)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for (int i = bytes.Length - 2; i >= 0; i--)
|
|
{
|
|
if (bytes[i] == 0xFF && bytes[i + 1] == 0xD9)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private static ImageCodecInfo GetJpegEncoder()
|
|
{
|
|
ImageCodecInfo[] codecs = ImageCodecInfo.GetImageEncoders();
|
|
foreach (var codec in codecs)
|
|
{
|
|
if (string.Equals(codec.MimeType, "image/jpeg", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return codec;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private static int GetIntSetting(string key, int defaultValue)
|
|
{
|
|
try
|
|
{
|
|
var raw = ConfigurationManager.AppSettings[key];
|
|
if (string.IsNullOrWhiteSpace(raw)) return defaultValue;
|
|
return int.TryParse(raw.Trim(), out var v) && v > 0 ? v : defaultValue;
|
|
}
|
|
catch
|
|
{
|
|
return defaultValue;
|
|
}
|
|
}
|
|
}
|
|
}
|