PowerShell开发小工具 · 四张照片拼成一张
小工具的设计与实现------选四张照片拼成一张照片。
很经典的应用情景,市面上有很多类似的小软件,特别是手机应用。为了方便学习巩固PowerShell,今天笔者使用它来实现。
【设计思路】
- 选择四张符合要求的照片或图片
[.jpg] [.png] - 准备画布,计算其子区域(画布四分之一)宽高比例
$subAspectRatio,然后与每张照片宽高比$imgAspectRatio作对比,在比例不变(不拉伸变形)的情况下尽可能铺满。 - 缩放照片,并拼合在一起
- 保存该画布,导出成图片格式存入硬盘中。
【设计要点】
计算子画布(子图区域),也就是四分之一画布大小的宽高比,与待拼合的照片的宽高比,两者进行比较。即subAspectRatio和imgAspectRatio
- 如果照片较宽,则按宽度缩放
- 如果照片较高,则按高度缩放
如下图所示:

如果理想状态,每张照片能铺满子图区域,如下图:

而有一种特殊情况,子区域是见方的(1:1宽高), 而每张图片也是见方的:

这两种情况直接等比缩放即可,而更多的情况是,子区域宽高比和每张照片的宽高比都不同,比如:

会发现有很多空白区域,没错,正是做了缩放处理。
算法实现:
# 计算子图宽高度
子图宽度 = 最终图片宽度 / 2
子图高度 = 最终图片高度 / 2
# 计算绘制位置
子图x坐标 = (每张照片[标号0 1 2 3] % 2) * 子图宽度
子图y坐标 = [math]::Floor(每张照片[标号0 1 2 3] / 2) * 子图高度
# 计算图片的宽高比
照片的宽高比 = 照片宽度 / 照片高度
子图的宽高比 = 子图宽度 / 子图高度
if (照片的宽高比 > 子图的宽高比) {
# 图片较宽,按宽度缩放
相比比例 = 子图宽度 / 照片宽度
待绘制图片宽度 = 子图宽度
待绘制图片高度 = [int](照片高度 * 相比比例)
待绘制图片y坐标 = 子图y坐标 + (子图宽度 - 待绘制图片高度) / 2
待绘制图片x坐标 = 子图x坐标
}
else {
# 图片较高,按高度缩放
相比比例 = 子图宽度 / 照片高度
待绘制图片高度 = 子图高度
待绘制图片宽度 = [int](照片宽度 * 相比比例)
待绘制图片x坐标 = 子图x坐标 + (子图宽度 - 待绘制图片宽度) / 2
待绘制图片y坐标 = 子图y坐标
}
# 创建缩放后的矩形区域
destRect = (
待绘制图片x坐标,
待绘制图片y坐标,
待绘制图片宽度,
待绘制图片高度
)
#缩放绘制
[将img即每张照片绘制到destRect区域内!]
【实际脚本】
- 导入程序集
System.Drawing,负责绘制任务 - 定义参数,如
文件夹地址、输出文件名、最终图片宽度和高度 - 获取文件夹内的照片(四张jpg或png)
- 创建目标画布
- 读取每张照片[遍历],按照上述算法进行计算,绘制在目标画布上
- 保存目标画布,导出成
.jpg格式的照片保存至文件夹内
# 加载必要的.NET绘图程序集(需确保系统已安装.NET Framework)
try {
Add-Type -AssemblyName System.Drawing
Write-Host "成功加载必要的.NET绘图程序集。"
}
catch {
Write-Error "加载.NET绘图程序集时出现错误: $_"
return
}
# 定义输入输出参数
$inputFolder = $PSScriptRoot # 使用 $PSScriptRoot 变量获取脚本所在文件夹
$outputFile = "combined.jpg" # 输出文件名
$targetWidth = 2000 # 最终图片宽度
$targetHeight = 2000 # 最终图片高度
# 从指定文件夹中获取前四张jpg或png图片
Write-Host "正在从 $inputFolder 文件夹中查找前四张jpg或png图片..."
$inputFiles = Get-ChildItem -Path $inputFolder -File | Where-Object { $_.Extension -match '\.(jpg|png)' } | Select-Object -First 4 | ForEach-Object { $_.FullName }
# 检查是否找到四张图片
if ($inputFiles.Count -ne 4) {
Write-Error "未在指定文件夹 $inputFolder 中找到四张jpg或png图片,仅找到 $($inputFiles.Count) 张。"
return
}
else {
Write-Host "成功找到四张图片:"
foreach ($file in $inputFiles) {
Write-Host "- $file"
}
}
# 创建目标画布
Write-Host "正在创建目标画布..."
try {
$combinedBitmap = New-Object System.Drawing.Bitmap($targetWidth, $targetHeight)
$graphics = [System.Drawing.Graphics]::FromImage($combinedBitmap)
$graphics.Clear([System.Drawing.Color]::White)
Write-Host "成功创建目标画布。"
}
catch {
Write-Error "创建目标画布时出现错误: $_"
return
}
# 计算每个子图区域尺寸
$subWidth = $targetWidth / 2
$subHeight = $targetHeight / 2
Write-Host "已计算每个子图区域尺寸:宽度 $subWidth,高度 $subHeight。"
# 遍历处理每张图片
for ($i = 0; $i -lt 4; $i++) {
Write-Host "正在处理图片 $($inputFiles[$i])..."
try {
$img = [System.Drawing.Image]::FromFile($inputFiles[$i])
# 计算绘制位置
$x = ($i % 2) * $subWidth
$y = [math]::Floor($i / 2) * $subHeight
# 计算图片的宽高比
$imgAspectRatio = $img.Width / $img.Height
$subAspectRatio = $subWidth / $subHeight
if ($imgAspectRatio -gt $subAspectRatio) {
# 图片较宽,按宽度缩放
$scale = $subWidth / $img.Width
$newWidth = $subWidth
$newHeight = [int]($img.Height * $scale)
$offsetY = $y + ($subHeight - $newHeight) / 2
$offsetX = $x
}
else {
# 图片较高,按高度缩放
$scale = $subHeight / $img.Height
$newHeight = $subHeight
$newWidth = [int]($img.Width * $scale)
$offsetX = $x + ($subWidth - $newWidth) / 2
$offsetY = $y
}
# 创建缩放后的矩形区域
$destRect = New-Object System.Drawing.Rectangle(
$offsetX, $offsetY,
$newWidth,
$newHeight
)
# 高质量缩放绘制
$graphics.InterpolationMode = [System.Drawing.Drawing2D.InterpolationMode]::HighQualityBicubic
$graphics.DrawImage($img, $destRect)
$img.Dispose()
Write-Host "成功处理图片 $($inputFiles[$i])。"
}
catch {
Write-Error "处理图片 $($inputFiles[$i]) 时出现错误: $_"
}
}
# 保存输出文件
Write-Host "正在保存拼接后的图片到 $outputFile..."
try {
$combinedBitmap.Save($outputFile, [System.Drawing.Imaging.ImageFormat]::Jpeg)
Write-Host "成功保存拼接后的图片到 $outputFile。"
}
catch {
Write-Error "保存拼接后的图片时出现错误: $_"
}
# 释放资源
Write-Host "正在释放资源..."
try {
$graphics.Dispose()
$combinedBitmap.Dispose()
Write-Host "成功释放资源。"
}
catch {
Write-Error "释放资源时出现错误: $_"
}
Write-Host "图片拼接完成,输出文件:$outputFile"
示例1:
给出四张示例照片或图片:

目标照片大小:2000 * 2000
将四张照片放入一个文件夹,然后将上述脚本保存成 .vbs 也放入其中,右键 - 使用PowerShell运行,之后得到同文件夹下的新照片:

示例2:
给出四张示例照片或图片:

目标照片大小:1600* 800
将四张照片放入一个文件夹,然后将上述脚本保存成 .vbs 也放入其中,右键 - 使用PowerShell运行,之后得到同文件夹下的新照片:

示例3:
给出四张示例照片或图片:

目标照片大小:2000 * 1450
将四张照片放入一个文件夹,然后将上述脚本保存成 .vbs 也放入其中,右键 - 使用PowerShell运行,之后得到同文件夹下的新照片:

示例4:
给出四张示例照片或图片:

目标照片大小:2000 * 2000
将四张照片放入一个文件夹,然后将上述脚本保存成 .vbs 也放入其中,右键 - 使用PowerShell运行,之后得到同文件夹下的新照片:

示例5:
给出四张示例照片或图片(四张规格大小一致,都是见方1:1):

目标照片大小:1600 * 1600
将四张照片放入一个文件夹,然后将上述脚本保存成 .vbs 也放入其中,右键 - 使用PowerShell运行,之后得到同文件夹下的新照片:

【结尾】
还是挺方便的,如果没有其他工具在手的话。当然,还有很多点值得优化,如:
- 图片的格式问题,支持更多格式
- 拼合的顺序,可以让用户自定义
- 可以让用户看到运行状态,方便调试修改
- 设配置文件,如目标照片大小、文件夹地址、生成文件类型等参数,可以更好的管理
总之,这是一次很好的开发经历,虽然脚本不算复杂,但是很实用,能充分体现脚本灵活易修改维护的特点。PowerShell,一直伴随你左右。。。
PowerShell开发小工具 · 四张照片拼成一张的更多相关文章
- Android 开发小工具之:Tools 属性 (转)
Android 开发小工具之:Tools 属性 http://blog.chengyunfeng.com/?p=755#ixzz4apLZhfmi 今天来介绍一些 Android 开发过程中比较有用但 ...
- 用adb命令组装PowerShell实用小工具——Android测试小助手
[本文出自天外归云的博客园] 简介 APP性能测试一般对以下几个方面进行测试: 1.启动时间(可以通过本工具测试): 2.CPU的占用(可以通过本工具测试): 3.内存的占用(可以通过本工具测试): ...
- Android 开发—— 小工具,大效率
欢迎大家关注腾讯云技术社区-博客园官方主页,我们将持续在博客园为大家推荐技术精品文章哦~ 作者:姚志锋 一.Hugo插件 -- 打印方法运行时间 首先申明下,此Hugo非 彼Hugo(Hugo是由Go ...
- xamarin开发的mac开发小工具集合
兄弟们我拖控件拖到了mac系统去了, 工具上传到百度网盘,下载地址 链接:https://pan.baidu.com/s/1Q64zoRjE3u66jJnzF8rhww提取码:ljx2 这款工具我是用 ...
- javaWeb开发小工具---MailUtils及其单元测试
本次介绍的是,在javaWeb开发中,我们不免会遇到发送邮件的需求,比如:用户注册账号,需要激活登录,以及服务器定期向会员发送礼品信息等.所以参考有关资料,写了这个MailUtils工具类. 1.Ma ...
- javaWeb开发小工具--MyCommonUtils
MyCommonUtils 参考一些资料,写了这个工具类.在这个工具类中,主要实现了2个方法: 1.生成随机的序列号 uuid(): 2.将Map中的数据封装到javaBean对象中toBean(Ma ...
- 分享原创powershell脚本小工具ctracert.ps1
----------[脚本介绍]----------- 脚本名称:ctracert.ps1软件名称:灰主牛 跟踪路由 归属地版 V1.0脚本作用:1跟踪路由.2显示归属地.(注意不带显示时间功能)脚本 ...
- Qt开发小工具之gif转换器(使用QMovie截取每一帧为QImage,然后用QFile另存为图片文件)
最近,QQ上好多各种gif表情.每一个都很经典呀..于是我就想把它转换成一张张静态图片...没学过ps.于是写了几行代码.完工.核心代码如下 主要是借助QMovie类.文件读取模式选择QMovie:: ...
- CSS便捷开发小工具汇总
1.Prefix free 可以帮助开发者省去编写各种CSS3属性前缀的工作,只需要在页面中引入prefixfree.js即可. 2. Normalize 是一个CSS Reset工具, 相比传统的R ...
- android 开发小工具收集
http://blog.csdn.net/tikitoo/article/details/51089422
随机推荐
- .NET 服务发现
.NET 服务发现 https://learn.microsoft.com/en-us/dotnet/core/extensions/service-discovery?tabs=dotnet-cli ...
- 在 .NET 环境下访问 SOAP 服务
在 .NET 环境下访问 SOAP 服务 SOAP 服务有着悠久的历史,目前仍然存在大量的 SOAP 服务,它是基于 HTTP 协议和 XML 技术的简单对象访问协议. 在 .NET Framewor ...
- 中电金信发布两款大模型产品,打通AI+应用“最后一公里”
近年来,以大模型为代表的人工智能技术已成为引领新一代产业变革的核心动力.2024年政府工作报告首次提出"人工智能+",要求"大力推进现代化产业体系建设,加快发展新质生产力 ...
- 2024年1月Java项目开发指南17:自动接口文档配置
Knife4j 文档 :https://doc.xiaominfo.com/ 有能力的建议自己去看文档配置,本文仅做参考,因为官方文档会更新,本文不会,以后说不定本文就过时了. ok,我们继续.虽然本 ...
- 问题解决:curl: (7) Failed to connect to raw.githubusercontent.com port 443: 拒绝连接
Ubuntu20.04下,安装Ros 指令curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key 报错:cur ...
- 关于Jetson nano (B02)如何部署Yolov8以及一些必要的知识点
一.前言 记录一个简单的安装和部署过程,尽管笔者也是按照教程来的,但奈何参考了很多教程,虽然写的都非常好,但是却很散,因此笔者这里想把这些教程的精华提炼出来,汇总并且写在正文处.还是老规矩,笔者也在学 ...
- [转]OpenCV4.8 GPU版本CMake编译详细步骤 与CUDA代码演示
导 读 本文将详细介绍如何使用CMake编译OpenCV4.8 CUDA版本并给出Demo演示,方便大家学习使用. CMake编译详细步骤 废话不多说,直接进入正题! [1]我使用的工具版本VS201 ...
- _findnext()调试中断,发生访问错误,错误定位到ntdll.dll
问题: 采用_findfirst和_findnext获取指定的文件夹下的文件时,_findnext()函数在调试时发生中断,发生访问错误,错误定位到ntdll.dll.错误提示如下所示: _findn ...
- 即时通讯技术文集(第32期):IM开发综合技术合集(Part5) [共12篇]
为了更好地分类阅读 52im.net 总计1000多篇精编文章,我将在每周三推送新的一期技术文集,本次是第32 期. [- 1 -] IM开发干货分享:如何优雅的实现大量离线消息的可靠投递 [链接] ...
- Swagger UI、RESTful简介
Swagger UI 简介 Swagger UI允许任何人(无论您是开发团队还是最终用户)都可以可视化API资源并与之交互,而无需任何实现逻辑.它是根据您的OpenAPI(以前称为Swagger)规范 ...