返回文章列表文章索引阅读全文
STARBLOG / 阅读手记
StarBlog博客开发笔记

StarBlog Development Notes (15): Generating Random-Size Images

Series Articles Developing a Blog Project with .NetCore: StarBlog - (1) Why Write Your Own Blog? Developing a Blog Project with .NetCore: StarBlog - (2) Environment Setup and Creating the Project Developing a Blog Project with .NetCore: StarBlog - (3) Model Design Developing a Blog Project with .NetCore: StarBlog - (4) Batch Import of Markdown Posts Developing a Blog Project with .NetCore: StarBl

曦远 (DealiAxy)约 21 分钟
目录8 个章节

Series Articles

Preface

Previously I wrote an article .NetCore Image Scaling and Cropping - Based on ImageSharp, which included an algorithm for generating random-size images, and that was also the original implementation in the StarBlog blog. However, when occasionally refreshing the page I noticed some images failed to load. After debugging I found it was throwing errors—the original algorithm had a bug. So I spent the weekend reimplementing it, and now it can be considered perfect~

The random-size image generation feature is currently used on article cards. Originally it used the service provided by LoremPicsum, but its servers are overseas, and after going live I found loading was too slow and images often failed to load, so I decided to implement a version myself. The foundation of this feature is ImageSharp from the article mentioned above.

image-20220704234244957

Approach

First, Let's Clarify the Requirements

  • Specify a directory as the image library location, put previously collected wallpaper images into it—a few hundred should be enough
  • Iterate to get the list of images in the library
  • Randomly pick one image
  • Scale or crop the image according to the specified size

There are also implementations of extended features such as specifying a random seed, statically mapping seeds to images, and so on.

Key Functionality

The key functionality is "scaling or cropping the image according to the specified size"

The difficulty is that when cropping and scaling images we must ensure:

  • Do not change the original aspect ratio of the image
  • Try to preserve the original content elements of the image as much as possible

In the first version I handled landscape and portrait images separately. (When the input size does not exceed the original image size) I first adjusted the side with the closer ratio to the same size, then cropped the middle portion. But the problem was obvious: if after resizing the other side's length was smaller than the input length, the image would be stretched, causing the aspect ratio to change.

After referring to several similar MATLAB and Python projects, I switched to a different approach:

  1. When the input size does not exceed the original image size, first crop according to the input size ratio, then resize
  2. If it exceeds the original image size, first scale the original image proportionally, then repeat step 1

An Example

For example, the original image is 1080 x 2340, and the input size is 400 x 200

First, we determine the size does not exceed the original, so no scaling is needed

Then comes "crop according to the input size ratio", simplify 400 x 200 to a 2 : 1 ratio

Crop a 2 : 1 size from the original image, i.e. 1080 x 540

Then resize the cropped image to 400 x 200, done!

Let's see the result

OriginalOutput (400x200)Output (200x300)
0a4894cc31cd124061ac2054ebe12421outputoutput

Although it uses a tiny bit more memory than the original approach, it genuinely improved the success rate of generating images, nice~

Code Implementation

I'll just show the code. Following the approach mentioned above, it's done in two steps, and the code is also cleaner than the original solution

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);
    }

It also uses calculating the image aspect ratio, which is simple: first find the greatest common divisor of the image width and height, then divide the width and height by this GCD respectively, and that's the ratio (i.e. simplifying a fraction)

Code for calculating the greatest common divisor

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;
}

Code for calculating the image ratio

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);
}

References

_EOF_

StarBlog Development Notes (15): Generating Random-Size Images

本文作者:
程序设计实验室
版权声明:
本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

相关文章

博客上线

终于上线啦! StarBlog博客项目从春节开始,跨越三个季节终于上线了~ 其实服务器这几天已经部署好了,然后我又测试了一会,暂时觉得OK,终于可以和大家分享了~ 博客地址是:blog.deali.cn 欢迎访问哈~ PS:友链模块即将完成,欢迎各位站长大佬们来交换友链~! 系列文章 顺便回顾一下已更新的12篇文章,接下来要介绍RESTFul接口和管理后台的前端开发了,欢迎继续关注~ 基于.Net

2022-06-20 · StarBlog博客开发笔记 · 约 1 分钟

StarBlog博客开发笔记(20):图片显示优化

前言 我的服务器带宽比较高,博客部署在上面访问的时候几乎没感觉有加载延迟,就没做图片这块的优化,不过最近有小伙伴说博客的图片加载比较慢,那就来把图片优化完善一下吧~ 目前有两个地方需要完善 图片瀑布流 图片缩略图 图片瀑布流 关于瀑布流之前的文章有介绍: 基于.NetCore开发博客项目 StarBlog - (10) 图片瀑布流 不过当时直接套用的 Bootstrap5 例子代码,有偶尔显示错位

2022-11-27 · StarBlog博客开发笔记 · 约 1 分钟

讨论

共 0 条

良言一句三冬暖,恶语伤人六月寒。

加载评论中…