系列文章
基于.NetCore開發博客項目 StarBlog - (1) 為什么需要自己寫一個博客?
基于.NetCore開發博客項目 StarBlog - (2) 環境準備和創建項目
基于.NetCore開發博客項目 StarBlog - (3) 模型設計
基于.NetCore開發博客項目 StarBlog - (4) markdown博客批量導入
基于.NetCore開發博客項目 StarBlog - (5) 開始搭建Web項目
基于.NetCore開發博客項目 StarBlog - (6) 頁面開發之博客文章列表
基于.NetCore開發博客項目 StarBlog - (7) 頁面開發之文章詳情頁面
基于.NetCore開發博客項目 StarBlog - (8) 分類層級結構展示
基于.NetCore開發博客項目 StarBlog - (9) 圖片批量導入
基于.NetCore開發博客項目 StarBlog - (10) 圖片瀑布流
基于.NetCore開發博客項目 StarBlog - (11) 實現訪問統計
基于.NetCore開發博客項目 StarBlog - (12) Razor頁面動態編譯
基于.NetCore開發博客項目 StarBlog - (13) 加入友情鏈接功能
基于.NetCore開發博客項目 StarBlog - (14) 實現主題切換功能
基于.NetCore開發博客項目 StarBlog - (15) 生成隨機尺寸圖片
...
前言
之前我寫了一篇 .NetCore實現圖片縮放與裁剪 - 基于ImageSharp,里面有生成尺寸隨機圖片的算法,同時也是StarBlog博客中原有的實現方式,不過偶爾刷新頁面的時候我注意到有些圖片加載不出來,調試了一下發現原來是報錯了,原本這個算法有bug。于是利用周末時間重新實現了一遍,這下可以說是完美了~
生成隨機尺寸圖片的功能目前用在文章卡片上,原本使用的是LoremPicsum提供的服務,但它的服務器在國外,上線之后發現加載太慢了,經常加載不出來,于是決定自己實現一版。功能基礎是上文提到的文章中的ImageSharp。

思路
先理一下需求
指定一個目錄作為圖片庫位置,把之前搜集的壁紙圖片放進去,大概放個幾百張就行了吧
遍歷獲取到庫中的圖片列表
隨機取出一張圖片
根據指定的尺寸縮放或裁剪圖片
其他的還有諸如指定隨機seed、將seed與圖片進行靜態映射等擴展功能的實現。
關鍵功能
關鍵功能在于「根據指定的尺寸縮放或裁剪圖片」
難點在于裁剪和縮放圖片要保證:
不改變圖片原有的比例
盡量保持圖片原有的內容元素
第一版我是將橫屏和豎屏的圖片分開處理,(在輸入尺寸不超過原圖尺寸的情況下)先把比例接近的邊調整成一樣的大小,再裁剪中間部分,不過問題也很明顯,如果調整大小之后另一條邊的長度小于輸入長度,那就會拉伸圖片,導致比例改變。
在參考幾個類似的MATLAB和Python項目之后,我換了別的思路:
在輸入尺寸不超過原圖尺寸的情況下,先按輸入的尺寸比例裁剪、再調整尺寸
如果超出原圖尺寸,則先按比例調整原圖的大小,再重復第一步
舉個例子
比如原圖是 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));elseimage.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);
}
參考資料
Python PIL圖片按比例裁剪:https://blog.csdn.net/lly1122334/article/details/122365539
python 等比例裁剪圖片:https://blog.csdn.net/chenping1993/article/details/110088858
C#基礎練習之求兩個數的最大公約數與最小公倍數:https://blog.csdn.net/maybe_ice/article/details/104328202