系列文章

前言

之前我写了一篇 .NetCore实现图片缩放与裁剪 - 基于ImageSharp,里面有生成尺寸随机图片的算法,同时也是StarBlog博客中原有的实现方式,不过偶尔刷新页面的时候我注意到有些图片加载不出来,调试了一下发现原来是报错了,原本这个算法有bug。于是利用周末时间重新实现了一遍,这下可以说是完美了~

生成随机尺寸图片的功能目前用在文章卡片上,原本使用的是LoremPicsum提供的服务,但它的服务器在国外,上线之后发现加载太慢了,经常加载不出来,于是决定自己实现一版。功能基础是上文提到的文章中的ImageSharp。

思路

先理一下需求

  • 指定一个目录作为图片库位置,把之前搜集的壁纸图片放进去,大概放个几百张就行了吧
  • 遍历获取到库中的图片列表
  • 随机取出一张图片
  • 根据指定的尺寸缩放或裁剪图片

其他的还有诸如指定随机seed、将seed与图片进行静态映射等扩展功能的实现。

关键功能

关键功能在于「根据指定的尺寸缩放或裁剪图片」

难点在于裁剪和缩放图片要保证:

  • 不改变图片原有的比例
  • 尽量保持图片原有的内容元素

第一版我是将横屏和竖屏的图片分开处理,(在输入尺寸不超过原图尺寸的情况下)先把比例接近的边调整成一样的大小,再裁剪中间部分,不过问题也很明显,如果调整大小之后另一条边的长度小于输入长度,那就会拉伸图片,导致比例改变。

在参考几个类似的MATLAB和Python项目之后,我换了别的思路:

  1. 在输入尺寸不超过原图尺寸的情况下,先按输入的尺寸比例裁剪、再调整尺寸
  2. 如果超出原图尺寸,则先按比例调整原图的大小,再重复第一步

举个例子

比如原图是 1080 x 2340 的尺寸,输入的图片是 400 x 200 尺寸

那第一步判断尺寸不超过原图,不需要缩放

然后是「按输入的尺寸比例裁剪」,把 400 x 200 化简成 2 : 1 的比例

在原图中截取 2 : 1 的大小,即 1080 x 540

然后再把截取的图片调整到 400 x 200,搞定!

看下效果

原图 输出(400x200) 输出(200x300)

虽然比一开始的方案更费一丢丢内存,但却实实在在提升了出图成功率,nice~

代码实现

直接上代码好了,根据上面提到的思路,分两步走,代码也比一开始的方案更整洁

async Task<(Image, IImageFormat)> GenerateSizedImageAsync(string imagePath, int width, int height) {
await using var fileStream = new FileStream(imagePath, FileMode.Open);
var (image, format) = await Image.LoadWithFormatAsync(fileStream); // 输出尺寸超出原图片尺寸,放大
if (width > image.Width && height > image.Height) {
image.Mutate(a => a.Resize(width, height));
}
else if (width > image.Width || height > image.Height) {
// 改变比例大的边
if (width / image.Width < height / image.Height)
image.Mutate(a => a.Resize(0, height));
else
image.Mutate(a => a.Resize(width, 0));
} // 将输入的尺寸作为裁剪比例
var (scaleWidth, scaleHeight) = GetPhotoScale(width, height);
var cropWidth = image.Width;
var cropHeight = (int) (image.Width / scaleWidth * scaleHeight);
if (cropHeight > image.Height) {
cropHeight = image.Height;
cropWidth = (int) (image.Height / scaleHeight * scaleWidth);
} var cropRect = new Rectangle((image.Width - cropWidth) / 2, (image.Height - cropHeight) / 2, cropWidth, cropHeight);
image.Mutate(a => a.Crop(cropRect));
image.Mutate(a => a.Resize(width, height)); return (image, format);
}

里面还用到了计算图片比例,很简单,先算出图片宽度和高度的最大公约数,然后宽高分别除以这个最大公约数,就是比例了(也就是化简分数)

计算最大公约数代码

private static int GetGreatestCommonDivisor(int m, int n) {
if (m < n) (n, m) = (m, n); while (n != 0) {
var r = m % n;
m = n;
n = r;
}
return m;
}

计算图片比例代码

private static (double, double) GetPhotoScale(int width, int height) {
if (width == height) return (1, 1);
var gcd = GetGreatestCommonDivisor(width, height);
return ((double)width / gcd, (double)height / gcd);
}

参考资料

基于.NetCore开发博客项目 StarBlog - (15) 生成随机尺寸图片的更多相关文章

  1. 基于.NetCore开发博客项目 StarBlog - (16) 一些新功能 (监控/统计/配置/初始化)

    系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 基于.NetC ...

  2. 基于.NetCore开发博客项目 StarBlog - (17) 自动下载文章里的外部图片

    系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 基于.NetC ...

  3. 基于.NetCore开发博客项目 StarBlog - (18) 实现本地Typora文章打包上传

    前言 九月太忙,只更新了三篇文章,本来这个功能是从九月初就开始做的,结果一直拖到现在国庆假期才有时间完善并且写文章~ 之前我更新了几篇关于 Python 的文章,有朋友留言问是不是不更新 .Net 了 ...

  4. 基于.NetCore开发博客项目 StarBlog - (19) Markdown渲染方案探索

    前言 笔者认为,一个博客网站,最核心的是阅读体验. 在开发StarBlog的过程中,最耗时的恰恰也是文章的展示部分功能. 最开始还没研究出来如何很好的使用后端渲染,所以只能先用Editor.md组件做 ...

  5. 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目

    系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 ... 基于. ...

  6. 基于.NetCore开发博客项目 StarBlog - (3) 模型设计

    系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 基于.NetC ...

  7. 基于.NetCore开发博客项目 StarBlog - (4) markdown博客批量导入

    系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 基于.NetC ...

  8. 基于.NetCore开发博客项目 StarBlog - (5) 开始搭建Web项目

    系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 基于.NetC ...

  9. 基于.NetCore开发博客项目 StarBlog - (6) 页面开发之博客文章列表

    系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 基于.NetC ...

随机推荐

  1. Kernel pwn 基础教程之 Heap Overflow

    一.前言 在如今的CTF比赛大环境下,掌握glibc堆内存分配已经成为了大家的必修课程.然而在内核态中,堆内存的分配策略发生了变化.笔者会在介绍内核堆利用方式之前先简单的介绍一下自己了解的内核内存分配 ...

  2. Arraylist面试三连问

    点赞在看,养成习惯,微信搜索「小大白日志」关注这个搬砖人. 本文在公众号文章已同步,还有各种一线大厂面试原题.我的学习系列笔记. Arraylist为什么要加transient? ArrayList源 ...

  3. 【题解】金牌导航-高斯消元/Luogu P3232 游走

    题目描述: 详细分析: 我们对于编号的分配,很明显可以发现如下的分配就是期望最小的:对经过的期望次数越大的边赋予更小的编号. 那么问题就转化为了怎么求一条边的经过的期望次数,我们发现边数非常大所以肯定 ...

  4. 配置Linux的时钟同步

    公众号关注 「开源Linux」 回复「学习」,有我为您特别筛选的学习资料~ Ubuntu系统默认的时钟同步服务器是ntp.ubuntu.com,Debian则是0.debian.pool.ntp.or ...

  5. CentOS 7上搭建Zabbix4.0

    开源Linux 一个执着于技术的公众号 zabbix介绍 Zabbix 是一个基于 WEB 界面的提供分布式系统监视以及网络监视功能的企业级的开源解决方案.它能监视各种网络参数,保证服务器系统的安全运 ...

  6. Vulnhub-Tomato靶机实战

    前言 靶机下载地址:https://www.vulnhub.com/entry/tomato-1,557/ KALI地址:192.168.94.108 靶机地址:192.168.94.30 一.信息收 ...

  7. JS&JQ

    1.css()设置单个或多个值: $(".iconList a").css("width", "32%")--单值:属性和值逗号分隔. $( ...

  8. Web3.0应用程序架构

    Web 3.0 应用程序(或"DApps")的架构与 Web 2.0 应用程序完全不同. 以博客园为例,这是一个简洁的博客网站,用户可以发布自己的内容并可以评论他人的内容进行互动. ...

  9. CMake技术总结

    在做算法部署的过程中,我们一般都是用C++开发,主要原因是C++的高效性,而构建维护一个大型C++工程的过程中,如何管理不同子模块之间的依赖.外部依赖库.头文件和源文件如何隔离.编译的时候又该如何相互 ...

  10. Packed Ciphertexts in LWE-based Homomorphic Encryption:解读

    本节内容记录阅读该论文的笔记 介绍 首先,介绍了两种明文"打包"的方法:PVW和SV PVW:对应论文(PVW:A framework for efficient and comp ...