首页 > 编程语言 >ASP.NET Core Library – ImageSharp

ASP.NET Core Library – ImageSharp

时间:2023-08-01 15:44:14浏览次数:66  
标签:Core ASP imageProcessing image using var new NET Image

前言

2021 年就写过一篇了, Asp.net core 学习笔记 Image processing (ImageSharp), 只是那时还是旧的写法, 这篇作为翻新和以后继续增加新功能的介绍.

ImageSharp 是 .NET 平台开源的图片处理 Library. 完全用 C# 来写, 从 0 开始写. 写了很多年, 目前算是比较 ok 用了. 2022 年开始也支持 webp 了哦.

 

参考

官网 Docs

 

安装

dotnet add package SixLabors.ImageSharp

 

查看图片信息

var fileFullPath = @"C:\keatkeat\projects\stooges-lib\stooges-aspnetcore\Project\wwwroot\uploaded-files\vertical-huawei.jpg";
using var image = Image.Load(fileFullPath);
var w = image.Width;
var h = image.Height;
var exifOrientation = image.Metadata.ExifProfile.GetValue(ExifTag.Orientation);

手机图片经常会出现反转问题, 可以查看 Exif Orientation 信息. 关于这个问题可以看我之前写的: Image Exif Orientation 图片方向信息

 

修改图片

复制代码
using var newImage = image.Clone(imageProcessing =>
{
    imageProcessing.AutoOrient();
    imageProcessing.Resize((int)Decimal.Divide(image.Width, 2), (int)Decimal.Divide(image.Width, 2));
    imageProcessing.Crop(new Rectangle(x: 150, y: 150, width: 150, height: 150));
    imageProcessing.Rotate(RotateMode.Rotate90);
    imageProcessing.Flip(FlipMode.Vertical);
});
newImage.SaveAsJpeg(
    @"C:\keatkeat\projects\stooges-lib\stooges-aspnetcore\Project\wwwroot\uploaded-files\vertical-huawei-croped.jpg", new SixLabors.ImageSharp.Formats.Jpeg.JpegEncoder
    {
        Quality = 85
    }
);
复制代码

Clone() 是复制一张来做修改, 如果想改原图拿就用 .Mutate(). 注: clone 记得使用 using 哦

AutoOrient 就是依据 Exif Orientation 来旋转和 flip 图片, 超方便的. 不需要自己搞, 转换之后 Exif Orientation 会变成 1 (哪怕之前是 0, 比如华为手机是 0)

resize, crop, rotate, flip 也是常见的图片操作, 还有一个 crop avatar 的 Github Sample.

修改 background color

png to jpg 默认 background color 是黑色的. 参考: Converting Png to Jpg give a color background and not white (Asp.Net Core)

var clonedImage = image.Clone(imageProcessing =>
{
    imageProcessing.BackgroundColor(Color.White);
});

想保存为 webp, 调用 SaveAsWebp 就可以了哦.

await image.SaveAsWebpAsync(Path.Combine(rootPath, "tifa2.webp"));

注: SaveAs... 是可以多次调用的. 它内部有处理好 stream reading 了.

 

水印 Watermark

水印的做法是在一张图上面, 添加上另一张图, 同时第二张图需要带有点 opacity 的效果.

复制代码
using var tifa = await Image.LoadAsync(@"C:\Users\keatk\Desktop\temp\review-avatar\tifa.jpg"); // 原图
using var logo = await Image.LoadAsync(@"C:\Users\keatk\Desktop\temp\review-avatar\Stooges Logo.png"); // 水印
// Clone 做出 new image
using var newImage = tifa.Clone(imageProcessing =>
{
    logo.Mutate(x => x.StgResizeWidth(200)); // 缩小 logo (这个不是必须的, 刚巧我的图片比较大而已)
    imageProcessing.DrawImage(
        logo,
        opacity: 0.5f,
        location: new Point(100, 100)
    );
    // imageProcessing 就是原图
    // DrawImage 就是在图上画画
    // logo 就是把水印画上去的意思
    // opacity 给 logo 加上透明度
    // location 是 x,y 坐标, 看你想把水印打到哪里
});
await newImage.SaveAsJpegAsync(@"C:\Users\keatk\Desktop\temp\review-avatar\new-image.jpg"); // 保存
复制代码

效果

 

New Image for object-fit: content 效果

CSS object-fit 效果, 把一张大图按比例缩小到框框里, 并且保留所有图片信息 (留白)

复制代码
using var tifa = await Image.LoadAsync(@"C:\Users\keatk\Desktop\temp\review-avatar\tifa.jpg");
using var newImage = new Image<Rgba32>(500, 500, backgroundColor: Rgba32.ParseHex("#fff")); // hex 或者 Color.White 都可以
tifa.Mutate(imageProcessing =>
{
    imageProcessing.Resize(width: 500, height: 500 * tifa.Height / tifa.Width); // 按比例缩小
});
newImage.Mutate(imageProcessing =>
{
    // 画到中间去
    imageProcessing.DrawImage(tifa, location: new Point(0, (500 / 2) - (tifa.Height / 2)), opacity: 1);
});
await newImage.SaveAsJpegAsync(@"C:\Users\keatk\Desktop\temp\review-avatar\tifa-contain.jpg");
复制代码

效果

 

 

扩展 ImageProcessing

虽然 imageProcessing 已经有很多好用的接口了, 但是不够上层.

resize width keep aspect ratio

比如我有一张图, dimension 是 1349 x 761

我想把它 resize to width 500, aspect ratio 保持

那可以这样写

var clonedImage = image.Clone(imageProcessing =>
{
    imageProcessing.Resize(width: 500, height: 500 * image.Height / image.Width);
});

它的缺点就是要写计算. 这就是所谓的不够上层. 那我们自己封装一下

封装 extensions

调用

var clonedImage = image.Clone(imageProcessing =>
{
    imageProcessing.MyResizeWidth(500);
});
extensions 复制代码
public static class ImageProcessingExtensions
{
    public static IImageProcessingContext MyResizeWidth(this IImageProcessingContext imageProcessing, int width)
    {
        imageProcessing.Resize(width, height: width * originalImage.Height / originalImage.Width);
        return imageProcessing;
    }
}
复制代码

这里遇到了一个问题, 算法需要 original image 的 dimension. 上面我们用了闭包才拿到的. 封装以后就拿不到了.

难不成要通过 parameter 传进来 (这样接口调用就扣分了丫). 还是可以从 IImageProcessingContext 里获取呢? 

在 IImageProcessingContext 如何获取 Image

源码追踪

首先去看看文档, 没发现类似的案例. 也不奇怪啦. 大部分文档都对扩展不友好的. 还是翻源码看看呗.

GetCurrentSize 最接近, 嗯... 通常 current size 已经足够我们用了, 但既然来了, 就再看看能不能拿到 original image 呗.

我们继续翻 Clone 的源码

AcceptVisitor 调用了 Accept

Accept 又调用了 visitor 的 Visit 并把 Image 自己传进去. 

 继续看 Visitor 的 visit 

到这里还算顺利, 我们要的 Image 最终有传入到 ImageProcessing 里头. 那样我们就可以通过 ImageProcessing 找回 Image 了.

这个 DefaultImageProcessorContext 就是我们最终使用的 ProcessingImage 了, 反射来确认一下

最后就是看这个 ProcessingImage 初始化, 看它把 Image 藏去哪里了.

很遗憾, 我们要的 Image 被放到了 private field. 没有任何接口可以拿到.

黑科技 – 反射获取 private field

虽然没有接口没有公开, 但可以通过反射, 强制去获取 private field

复制代码
public static class ImageProcessingExtensions
{
    public static Image MyGetOriginalImage(this IImageProcessingContext imageProcessing)
    {var sourceField = imageProcessing.GetType().GetField("source", BindingFlags.NonPublic | BindingFlags.Instance)!;
        return (sourceField.GetValue(imageProcessing) as Image)!;
    }
    public static IImageProcessingContext ResizeWidth(this IImageProcessingContext imageProcessing, int width)
    {
        var originalImage = imageProcessing.MyGetOriginalImage();
        imageProcessing.Resize(width, height: width * originalImage.Height / originalImage.Width);
        return imageProcessing;
    }
}
复制代码

这样就可以了. 但是要记得哦, 这个是 hacking way, 每次升级都有可能引发 unknown breaking change. 一定要特别注意哦.

p.s. rezie 用 current size 通常才是正确的需求, 上面拿 original 只是随便举个例子而已.

 

写字 DrawText

参考:

Docs – Fonts

Github – Sample

How to add text to an image with C# in dotnet

写字功能目前还处于 beta version, 需要用到 2 个的插件, SixLabors.Fonts 和 SixLabors.ImageSharp.Drawing

dotnet add package SixLabors.Fonts --version 1.0.0-beta19
dotnet add package SixLabors.ImageSharp.Drawing --version 1.0.0-beta15

下载字体

.ttf 肯定可以, .otf 我没有测试. 我是用 Google Font 做测试.

Load Image

复制代码
public class Program
{
    public static void Main()
    {
        var rootPath = Path.Combine(AppContext.BaseDirectory, @"..\..\..\");
        var yangmi = new FileInfo(Path.Combine(rootPath, "yangmi.jpg"));
        using var image = Image.Load(yangmi.FullName);
    }
}
复制代码

Setup Font

var collection = new FontCollection();
var family = collection.Add(Path.Combine(rootPath, "Caveat-Bold.ttf"));
var font = family.CreateFont(128, FontStyle.Bold);

导入字体, 并且设置 font style

{
  font-family: 'Caveat';
  font-size: 128px;
  font-weight: 700;
}

不想导入, 也可以用 system font

var font = SystemFonts.CreateFont("Times New Roman", 128, FontStyle.Bold);

Setup Font Drawing Options

var textOptions = new TextOptions(font)
{
    Origin = new PointF(64, 64),
    WrappingLength = 3840f * 0.25f,
    HorizontalAlignment = HorizontalAlignment.Left,
};

Origin 是要 draw 的 coordinate (x,y)

WrappingLength 是 frame 的 width. 多少之后要 wrap (字掉去下一行)

HorizontalAlignment 是从左写到右

Setup Brush

var brush = Brushes.Solid(Color.White);

Brush 负责调颜色

DrawText

string text = "Yang Mi is a Chinese actress, singer, and model known for her roles in popular Chinese TV dramas and films.";
using var newImage = image.Clone(ctx =>
{
    ctx.DrawText(options, text, brush);
});
await newImage.SaveAsJpegAsync(Path.Combine(rootPath, "yangmi-name.jpg"));

把字和 setting 丢进去就可以了.

效果

漂亮吧

 

Text Background Color

继续上一 part 的例子. 我们要添加 overlay background 到 text 上.

首先得画出 overlay. 

overlay 的 width, height depend on text 内容. 所以需要计算.

Calc Text Szie

复制代码
var font = SystemFonts.CreateFont("Times New Roman", 128, FontStyle.Bold);
var text = "Yang Mi is a Chinese actress, singer, and model known for her roles in popular Chinese TV dramas and films.";
var textOptions = new TextOptions(font)
{
    Origin = new PointF(16, 16),
    WrappingLength = 3840f * 0.25f,
    HorizontalAlignment = HorizontalAlignment.Left,
};
var textSize = TextMeasurer.Measure(text, textOptions);
Console.Write(textSize.Width + "x" + textSize.Height);
复制代码

Draw Text Overlay

复制代码
using var overlay = new Image<Rgba32>((int)textSize.Width + 64, (int)textSize.Height + 64);
overlay.Mutate(ctx =>
{
    ctx.Fill(Color.Red.WithAlpha(0.5f));
    var textBrush = Brushes.Solid(Color.White);
    ctx.DrawText(textOptions, text, textBrush);
});
复制代码

创建一张图, 通过 fill 设置 background color, 再把字上进去.

注: background 是有 opacity 的哦, 但字没有.

Draw into Image

最后把 overlay 图片换进原图. 也就是上面做 watermark 的方法.

using var newImage = image.Clone(ctx =>
{
    ctx.DrawImage(overlay, location: new Point(64, 64), opacity: 1);
});
await newImage.SaveAsJpegAsync(Path.Combine(rootPath, "yangmi-name.jpg"));

效果

我们不可以直接在原图上 Fill. 它会覆盖掉原图, 而且没有 opacity 效果.

using var newImage = image.Clone(ctx =>
{
    ctx.Fill(Color.Red.WithAlpha(0.5f));
    var textBrush = Brushes.Solid(Color.White);
    ctx.DrawText(textOptions, text, textBrush);
});

上面这样是不 ok 的. 必须用 DrawImage 的方式来做. (我不清楚原因...)

标签:Core,ASP,imageProcessing,image,using,var,new,NET,Image
From: https://www.cnblogs.com/wtlcy/p/17596698.html

相关文章

  • MobileNetV2 :Inverted Residuals and Linear Bottlenecks
    论文链接|https://arxiv.org/abs/1704.04861论文源码|https://github.com/Randl/MobileNet2-pytorch/摘要:介绍了一种新的移动端架构——MobileNetV2,其在多任务和基准以及不同模型大小的范围上进一步刷新了移动端模型的当前最佳性能。介绍了如何通过全新框架SSDLite将这......
  • .net 7 验证码功能
    usingSixLabors.ImageSharp还需要用到2个的插件,SixLabors.Fonts和SixLabors.ImageSharp.Drawing通过CLI手动安装dotnetaddpackageSixLabors.ImageSharp.Drawing--version1.0.0-beta15dotnetaddpackageSixLabors.Fonts--version1.0.0-beta19usingSystem.Text;......
  • netfilter中有哪些挂载点(hook points)?
    5个挂载点,挂载点有各种数据包处理的规则。 分别是:PREROUTINGINPUTFORWAROUTPUTPOSTROUTING 这些挂接点(hookpoints),是数据包处理的不同的阶段。 每个挂节点上,都可以挂载不同的包的处理规则,从而实现对数据包的处理和过滤。 ......
  • ADO.NET的最佳实践技巧
    ADO.NET的最佳实践技巧简介本文为您提供了在MicrosoftADO.NET应用程序中实现和获得最佳性能、可伸缩性以及功能的最佳解决方案;同时也讲述了使用ADO.NET中可用对象的最佳实践;并提出一些有助于优化ADO.NET应用程序设计的建议。本文包含:•有关.NET框架包含的.NET框架数据......
  • [转].Net2.0连接PG数据注意事项
    .Net2.0连接PG数据注意事项第一次用.net操作PG[.NET2.0]一:Npgsql版本问题1:如果是.net2.0  建议用2.0.11.0[NuGet搜索npgsql第一个的最低版本]如果是.net3.5 最高只能到2.2.62.2.7就需要.net4了二:SqlHelp操作就像操作SQLServer那样操作,只是几个类换一下。SqlConnect......
  • profinet转CCLINK IE FIELD BASIC 总线网关profinet接口
    协议的不同,数据难以交换,这是生产管理系统中一直存在的难题。然而,现在有一款神奇的产品出现了,它能够将各种profinet 设备接入到CCLINKIEFIELDBASIC 网络中。这就是捷米JM-PNM-CCLKIE,一款CCLINKIEFIELDBASIC 从站功能的通讯网关。它不仅连接到CCLINKIEFIELDBASIC ......
  • ASP.NET CORE下日志NLOG使用
    ASP.NETCORE下日志NLOG使用摘自《深入浅出ASP.NETCORE》NUGET:NLog.Web.AspNetCore4.9.0根目录下nlog.config<?xmlversion="1.0"encoding="utf-8"?><nlogxmlns="http://www.nlog-project.org/schemas/NLog.xsd"xmlns:xsi="htt......
  • asp.net core之日志
    日志记录在应用程序开发中起着至关重要的作用,它可以帮助开发人员诊断和调试问题,同时也是监控和性能优化的重要工具。ASP.NETCore提供了强大且灵活的日志记录功能,本文将详细介绍ASP.NETCore中的日志记录,包括日志配置、日志类别级别、使用场景以及日志记录提供程序。日志配置......
  • 7种方法!教你如何使用Telerik UI for ASP.NET定制编辑器
    TelerikUIforASP.NETCore是用于跨平台响应式Web和云开发的最完整的UI工具集,拥有超过60个由KendoUI支持的ASP.NET核心组件。它的响应式和自适应的HTML5网格,提供从过滤、排序数据到分页和分层数据分组等100多项高级功能。TelerikUIforASP.NETCore的编辑器是一个多功能的所......
  • 6.6 卷积神经网络LeNet
    LeNet,它是最早发布的卷积神经网络之一,因其在计算机视觉任务中的高效性能而受到广泛关注。这个模型是由AT&T贝尔实验室的研究员YannLeCun在1989年提出的(并以其命名),目的是识别图像中的手写数字。总体来看,LeNet由两个部分组成:卷积编码器:由两个卷积层组成;全连接层密集块:由三......