ASP.NET Core SixLabors.ImageSharp v1.0 的图像实用程序类 web示例

这个小型实用程序库需要将 NuGet SixLabors.ImageSharp包(版本 1.0.4)添加到.NET Core 3.1/ .NET 6 / .NET 8项目中。它与Windows、Linux和 MacOS兼容。

这已针对 ImageSharp v3.0.1 进行了重新设计

它可以根据百万像素数或长度乘以宽度来调整图像大小,并根据需要保留纵横比。

它根据EXIF数据旋转/翻转图像。这是为了适应移动设备。

它还创建散点图和直方图。

另请参阅:MVC 应用程序的位图图像创建和下载

using SixLabors.ImageSharp;

using SixLabors.ImageSharp.Metadata.Profiles.Exif;

using SixLabors.ImageSharp.Processing;

using SixLabors.ImageSharp.PixelFormats;

using SixLabors.ImageSharp.Formats.Png;

using System.IO;

using System;

using SixLabors.ImageSharp.Formats.Jpeg;

namespace ImageUtil

{

public class GetSize

{

public GetSize(Stream stream)

{

using (Image iOriginal = Image.Load(stream))

{

stream.Position = 0;

Width = iOriginal.Width;

Height = iOriginal.Height;

}

}

/// <summary>

/// The width of the image specified in the class constructor's Stream parameter

/// </summary>

public int Width { get; }

/// <summary>

/// The height of the image specified in the class constructor's Stream parameter

/// </summary>

public int Height { get; }

}

public static class Resize

{

/// <summary>

/// Resize and save an image to a Stream specifying its new width and height

/// </summary>

public static void SaveImage(Stream imageStream, int newWidth, int newHeight, bool preserveImageRatio, Stream saveToStream, int jpegQuality = 100)

{

using (Image iOriginal = Image.Load(imageStream))

{

imageStream.Position = 0;

if (preserveImageRatio)

{

float percentWidth = newWidth / (float)iOriginal.Width;

float percentHeight = newHeight / (float)iOriginal.Height;

float percent = percentHeight < percentWidth ? percentHeight : percentWidth;

newWidth = (int)Math.Round(iOriginal.Width * percent, 0);

newHeight = (int)Math.Round(iOriginal.Height * percent, 0);

}

resize(imageStream, iOriginal, newWidth, newHeight, saveToStream, jpegQuality);

}

}

/// <summary>

/// Resize and save an image to a Stream specifying the number of pixels to resize to

/// </summary>

public static void SaveImage(Stream imageStream, int newNumberOfPixels, Stream saveToStream, int jpegQuality = 100)

{

using (Image iOriginal = Image.Load(imageStream))

{

imageStream.Position = 0;

double ratio = Math.Sqrt(newNumberOfPixels / (double)(iOriginal.Width * iOriginal.Height));

resize(imageStream, iOriginal, (int)Math.Round(iOriginal.Width * ratio, 0), (int)Math.Round(iOriginal.Height * ratio, 0), saveToStream, jpegQuality);

}

}

private static void resize(Stream origSource, Image image, int newWidth, int newHeight, Stream saveTo, int jpegQuality)

{

image.Mutate(x => x.Resize(newWidth, newHeight));

transformImage(image); // NOTE: transform image AFTER resizing it!!!

var format = Image.DetectFormat(origSource);

if (format.Name.ToLower() == "jpeg")

{

var encoder = new JpegEncoder();

encoder.Quality = jpegQuality;

image.SaveAsJpeg(saveTo, encoder);

}

else

image.Save(saveTo, format);

}

private static void transformImage(Image image)

{

IExifValue exifOrientation = image.Metadata?.ExifProfile?.GetValue(ExifTag.Orientation);

if (exifOrientation == null)

return;

RotateMode rotateMode;

FlipMode flipMode;

setRotateFlipMode(exifOrientation, out rotateMode, out flipMode);

image.Mutate(x => x.RotateFlip(rotateMode, flipMode));

image.Metadata.ExifProfile.SetValue(ExifTag.Orientation, (ushort)1);

}

private static void setRotateFlipMode(IExifValue exifOrientation, out RotateMode rotateMode, out FlipMode flipMode)

{

var orientation = (ushort)exifOrientation.GetValue();

switch (orientation)

{

case 2:

rotateMode = RotateMode.None;

flipMode = FlipMode.Horizontal;

break;

case 3:

rotateMode = RotateMode.Rotate180;

flipMode = FlipMode.None;

break;

case 4:

rotateMode = RotateMode.Rotate180;

flipMode = FlipMode.Horizontal;

break;

case 5:

rotateMode = RotateMode.Rotate90;

flipMode = FlipMode.Horizontal;

break;

case 6:

rotateMode = RotateMode.Rotate90;

flipMode = FlipMode.None;

break;

case 7:

rotateMode = RotateMode.Rotate90;

flipMode = FlipMode.Vertical;

break;

case 8:

rotateMode = RotateMode.Rotate270;

flipMode = FlipMode.None;

break;

default:

rotateMode = RotateMode.None;

flipMode = FlipMode.None;

break;

}

}

}

/* CREATE A SCATTER PLOT JPEG/PNG IMAGE */

public static class ScatterPlot

{

static readonly Rgba32 WHITE = new Rgba32(255, 255, 255);

static readonly Rgba32 BLACK = new Rgba32(0, 0, 0);

static readonly Rgba32 RED = new Rgba32(255, 0, 0);

static readonly Rgba32 BLUE = new Rgba32(0, 0, 255);

static readonly Rgba32 GREEN = new Rgba32(0, 192, 0);

static readonly Rgba32 PURPLE = new Rgba32(128, 0, 128);

static readonly Rgba32 ORANGE = new Rgba32(255, 164, 0);

static readonly Rgba32 YELLOW = new Rgba32(255, 225, 0);

static readonly Rgba32 MAGENTA = new Rgba32(255, 0, 255);

static readonly Rgba32 AQUA = new Rgba32(0, 225, 255);

static readonly Rgba32 BLUEJEAN = new Rgba32(0, 128, 255);

static readonly Rgba32 BROWN = new Rgba32(150, 75, 50);

static readonly Rgba32 CHARTREUSE = new Rgba32(223, 255, 0);

static readonly Rgba32 DODGERBLUE = new Rgba32(30, 144, 255);

static readonly Rgba32 NAVY = new Rgba32(0, 0, 128);

static readonly Rgba32 DARKRED = new Rgba32(139, 0, 0);

static readonly Rgba32[] colors = new Rgba32[] { WHITE, BLACK, RED, BLUE, GREEN, PURPLE, ORANGE, YELLOW, MAGENTA, AQUA, BLUEJEAN, BROWN, DODGERBLUE, CHARTREUSE, NAVY, DARKRED };

public static void CreateJPEG(Stream stream, ScatterPlotColors backgroundColor, IEnumerable<PlotPoint> points, int width, int height, int pointSize, int jpegQuality = 100)

{

using (var bmp = new Image<Rgba32>(width / pointSize + 6, height / pointSize + 6, colors[(int)backgroundColor]))

create(stream, width, height, bmp, points, pointSize, new JpegEncoder() { Quality = jpegQuality });

}

public static void CreateJPEG(string filename, ScatterPlotColors backgroundColor, IEnumerable<PlotPoint> points, int width, int height, int pointSize, int jpegQuality = 100)

{

using (var stream = File.Create(filename))

CreateJPEG(stream, backgroundColor, points, width, height, pointSize, jpegQuality);

}

public static void CreatePNG(Stream stream, IEnumerable<PlotPoint> points, int width, int height, int pointSize)

{

using (var bmp = new Image<Rgba32>(width / pointSize + 6, height / pointSize + 6))

create(stream, width, height, bmp, points, pointSize, new PngEncoder());

}

public static void CreatePNG(string filename, IEnumerable<PlotPoint> points, int width, int height, int pointSize)

{

using (var stream = File.Create(filename))

CreatePNG(stream, points, width, height, pointSize);

}

private static double normalize(float min, float max, float value)

{

double x = max - min;

if(x == 0)

return 0;

return (value - min) / x;

}

private static void create(Stream stream, int width, int height, Image<Rgba32> bmp, IEnumerable<PlotPoint> points, int pointSize, SixLabors.ImageSharp.Formats.IImageEncoder imageEncoder)

{

float xMax = float.MinValue, xMin = float.MaxValue, yMax = float.MinValue, yMin = float.MaxValue;

foreach (var pt in points)

{

xMax = Math.Max(pt.x, xMax);

xMin = Math.Min(pt.x, xMin);

yMax = Math.Max(pt.y, yMax);

yMin = Math.Min(pt.y, yMin);

}

float xDelta = Math.Max(1, xMax - xMin), yDelta = Math.Max(1, yMax - yMin);

int w = width / pointSize;

int h = height / pointSize;

foreach (var pt in points)

{

int x = (int)(w * normalize(xMin, xMax, pt.x));

int y = (int)(h * normalize(yMin, yMax, pt.y));

bmp[x + 3, y + 3] = colors[((int)pt.color) % colors.Length];

}

bmp.Mutate(x => x.Flip(FlipMode.Vertical));

bmp.Mutate(x => x.Resize(width, height));

bmp.Save(stream, imageEncoder);

}

}

public class PlotPoint

{

public ScatterPlotColors color { get; }

public float x { get; }

public float y { get; }

public PlotPoint(float x, float y, ScatterPlotColors color)

{

this.x = x;

this.y = y;

this.color = color;

}

}

public enum ScatterPlotColors

{

WHITE,

BLACK,

RED,

BLUE,

GREEN,

PURPLE,

ORANGE,

YELLOW,

MAGENTA,

AQUA,

BLUEJEAN,

BROWN,

CHARTREUSE,

DODGERBLUE,

NAVY,

DARKRED

}

/* CREATE A PNG HISTOGRAM OF AN IMAGE */

public static class Histogram

{

/// <summary>

/// Create a histogram from the data in a stream

/// </summary>

public static MemoryStream CreatePNG(Stream stream, int width, int height, LRGB lrgb, byte alphaChannel = 128, bool clipBlackAndWhite = true, byte luminanceShade = 255)

{

using (var bmp = Image<Rgb24>.Load(stream))

{

return create(bmp, width, height, lrgb, alphaChannel, clipBlackAndWhite, luminanceShade);

}

}

/// <summary>

/// Create a histogram from the data in a file

/// </summary>

public static MemoryStream CreatePNG(string filename, int width, int height, LRGB lrgb, byte alphaChannel = 128, bool clipBlackAndWhite = true, byte luminanceShade = 255)

{

using (var bmp = Image<Rgb24>.Load(filename))

{

return create(bmp, width, height, lrgb, alphaChannel, clipBlackAndWhite, luminanceShade);

}

}

private static MemoryStream create(Image bmp, int width, int height, LRGB lrgb, byte alpha, bool clip, byte shade)

{

ulong[] lumin = new ulong[256];

ulong[] red = new ulong[256];

ulong[] green = new ulong[256];

ulong[] blue = new ulong[256];

var bred = (lrgb & LRGB.RED) != 0;

var bgreen = (lrgb & LRGB.GREEN) != 0;

var bblue = (lrgb & LRGB.BLUE) != 0;

var blumin = (lrgb == LRGB.LUMINANCE);

int w = bmp.Width;

int h = bmp.Height;

var bmp2 = bmp.CloneAs<Rgb24>();

for (int y = 0; y < h; y++)

{

Span<Rgb24> pixelRow = bmp2.GetPixelRowSpan(y);

for (int x = 0; x < w; x++)

{

var c = pixelRow[x];

lumin[(int)Math.Round((c.R + c.G + c.B) / 3.0)]++;

red[c.R]++;

green[c.G]++;

blue[c.B]++;

}

}

ulong max = 0;

int a = (clip ? 1 : 0), b = (clip ? 255 : 256);

for (int i = a; i < b; i++)

{

if (!blumin)

{

if (bred)

if (max < red[i])

max = red[i];

if (bgreen)

if (max < green[i])

max = green[i];

if (bblue)

if (max < blue[i])

max = blue[i];

}

else if (max < lumin[i])

max = lumin[i];

}

double HEIGHTFACTOR = 256.0 / max;

if (blumin)

{

using (var bmplum = new Image<Rgba32>(256, 256))

{

var penlum = new VerticalPen(new Rgba32(shade, shade, shade, alpha));

for (int i = 0; i < 256; i++)

penlum.Draw(bmplum, i, (int)(lumin[i] * HEIGHTFACTOR));

bmplum.Mutate(x => x.Resize(width, height));

MemoryStream ms = new MemoryStream();

bmplum.Save(ms, new PngEncoder());

return ms;

}

}

else

{

using (var bmppre = new Image<Rgba32>(256, 256))

{

Image<Rgba32>? bmpred = null, bmpgreen = null, bmpblue = null;

VerticalPen? penred = null, pengreen = null, penblue = null;

if (bred)

{

bmpred = new Image<Rgba32>(256, 256);

penred = new VerticalPen(new Rgba32(255, 0, 0, alpha));

}

if (bgreen)

{

bmpgreen = new Image<Rgba32>(256, 256);

pengreen = new VerticalPen(new Rgba32(0, 255, 0, alpha));

}

if (bblue)

{

bmpblue = new Image<Rgba32>(256, 256);

penblue = new VerticalPen(new Rgba32(0, 0, 255, alpha));

}

for (int i = 0; i < 256; i++)

{

if (bred)

penred.Draw(bmpred, i, (int)(red[i] * HEIGHTFACTOR));

if (bgreen)

pengreen.Draw(bmpgreen, i, (int)(green[i] * HEIGHTFACTOR));

if (bblue)

penblue.Draw(bmpblue, i, (int)(blue[i] * HEIGHTFACTOR));

}

if (bred)

{

bmppre.Mutate(x => x.DrawImage(bmpred, 1));

bmpred.Dispose();

}

if (bgreen)

{

bmppre.Mutate(x => x.DrawImage(bmpgreen, 1));

bmpgreen.Dispose();

}

if (bblue)

{

bmppre.Mutate(x => x.DrawImage(bmpblue, 1));

bmpblue.Dispose();

}

bmppre.Mutate(x => x.Resize(width, height));

MemoryStream ms = new MemoryStream();

bmppre.Save(ms, new PngEncoder());

return ms;

}

}

}

internal class VerticalPen

{

private readonly Rgba32 color;

public VerticalPen(Rgba32 color)

{

this.color = color;

}

public void Draw(Image<Rgba32> bmp, int row, int height)

{

if (height <= bmp.Height)

for (int y = height - 1; y >= 0; y--)

bmp[row, bmp.Height - 1 - y] = color;

}

}

public enum LRGB

{

LUMINANCE = 0,

RED = 1,

GREEN = 2,

BLUE = 4,

REDBLUE = 1 | 4,

REDGREEN = 1 | 2,

BLUEGREEN = 2 | 4,

REDGREENBLUE = 1 | 2 | 4

}

}

}

示例用法

另请参阅:控制台应用程序示例

这是MVC Web 应用程序中的视图。

@{

ViewData["Title"] = "/Home/UploadImages";

}

<div class="text-center">

<h1 class="display-4">UploadImages</h1>

<form enctype="multipart/form-data" method="post">

<div>

<input type="file" name="images" accept="image/jpeg, image/gif, image/png" multiple />

</div>

<div>

<button type="submit" class="btn btn-primary">UPLOAD</button>

</div>

</form>

@if(Model is int)

{

<p>@Model images saved.</p>

}

</div>

现在介绍控制器代码:

using Microsoft.AspNetCore.Http;

using Microsoft.AspNetCore.Mvc;

using Microsoft.AspNetCore.Mvc.TagHelpers;

using Microsoft.Extensions.Logging;

using System;

using System.Collections.Generic;

using System.Diagnostics;

using System.Linq;

using System.Threading.Tasks;

using WebApplication1.Models;

namespace WebApplication1.Controllers

{

public class HomeController : Controller

{

private readonly ILogger<HomeController> _logger;

public HomeController(ILogger<HomeController> logger)

{

_logger = logger;

}

public IActionResult UploadImages()

{

return View();

}

HttpPost

DisableRequestSizeLimit\] // NOTE: NOT RECOMMENDED - SET A LIMIT public IActionResult UploadImages(IFormCollection form) { int count = 0; foreach(var image in form.Files) { if(image.ContentType.StartsWith("image/")) { using (var openfs = image.OpenReadStream()) { string filename = "thumbnail-" + image.FileName; if (System.IO.File.Exists(filename)) System.IO.File.Delete(filename); using (var savefs = System.IO.File.OpenWrite(filename)) { // NOTE: RESIZE IMAGE TO 10K PIXEL THUMBNAIL; THIS WILL ALSO FLIP AND ROTATE IMAGE AS NEEDED ImageUtil.Resize.SaveImage(openfs, 10000, savefs); count++; } } } } return View(count); } public IActionResult Index() { return View(); } public IActionResult Privacy() { return View(); } \[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)

public IActionResult Error()

{

return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });

}

}

}

如果您喜欢此文章,请收藏、点赞、评论,谢谢,祝您快乐每一天。

相关推荐
gCode Teacher 格码致知4 小时前
《Asp.net Mvc 网站开发》复习试题
后端·asp.net·mvc
oMMh1 天前
使用C# ASP.NET创建一个可以由服务端推送信息至客户端的WEB应用(2)
前端·c#·asp.net
编程乐趣1 天前
基于.Net Core开发的GraphQL开源项目
后端·.netcore·graphql
田辛 | 田豆芽1 天前
【ASP.net】在Windows 11上安装IIS并测试C# Web项目的踩坑实录
windows·c#·asp.net
吾门2 天前
机器视觉开发教程——C#如何封装海康工业相机SDK调用OpenCV/YOLO/VisionPro/Halcon算法
图像处理·opencv·计算机视觉·c#·.net·.netcore·visual studio
Kookoos3 天前
ABP vNext + EF Core 实战性能调优指南
数据库·后端·c#·.net·.netcore
[email protected]4 天前
ASP.NET Core 中实现 Markdown 渲染中间件
后端·中间件·asp.net·.netcore
界面开发小八哥4 天前
DevExtreme JS & ASP.NET Core v25.1新功能预览 - 全新的Stepper组件
javascript·asp.net·界面控件·devexpress·ui开发·devextreme
海天胜景4 天前
HTTP Error 500.31 - Failed to load ASP.NET Core runtime
后端·asp.net
海天胜景4 天前
Asp.Net Core IIS发布后PUT、DELETE请求错误405
数据库·后端·asp.net