小工具的设计与实现------选四张照片拼成一张照片。

很经典的应用情景,市面上有很多类似的小软件,特别是手机应用。为了方便学习巩固PowerShell,今天笔者使用它来实现。

【设计思路】

  • 选择四张符合要求的照片或图片[.jpg] [.png]
  • 准备画布,计算其子区域(画布四分之一)宽高比例 $subAspectRatio,然后与每张照片宽高比 $imgAspectRatio 作对比,在比例不变(不拉伸变形)的情况下尽可能铺满。
  • 缩放照片,并拼合在一起
  • 保存该画布,导出成图片格式存入硬盘中。

【设计要点】

计算子画布(子图区域),也就是四分之一画布大小的宽高比,与待拼合的照片的宽高比,两者进行比较。即subAspectRatioimgAspectRatio

  • 如果照片较宽,则按宽度缩放
  • 如果照片较高,则按高度缩放

如下图所示:

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

而有一种特殊情况,子区域是见方的(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运行,之后得到同文件夹下的新照片:

【结尾】

还是挺方便的,如果没有其他工具在手的话。当然,还有很多点值得优化,如:

  1. 图片的格式问题,支持更多格式
  2. 拼合的顺序,可以让用户自定义
  3. 可以让用户看到运行状态,方便调试修改
  4. 设配置文件,如目标照片大小、文件夹地址、生成文件类型等参数,可以更好的管理

总之,这是一次很好的开发经历,虽然脚本不算复杂,但是很实用,能充分体现脚本灵活易修改维护的特点。PowerShell,一直伴随你左右。。。

PowerShell开发小工具 · 四张照片拼成一张的更多相关文章

  1. Android 开发小工具之:Tools 属性 (转)

    Android 开发小工具之:Tools 属性 http://blog.chengyunfeng.com/?p=755#ixzz4apLZhfmi 今天来介绍一些 Android 开发过程中比较有用但 ...

  2. 用adb命令组装PowerShell实用小工具——Android测试小助手

    [本文出自天外归云的博客园] 简介 APP性能测试一般对以下几个方面进行测试: 1.启动时间(可以通过本工具测试): 2.CPU的占用(可以通过本工具测试): 3.内存的占用(可以通过本工具测试): ...

  3. Android 开发—— 小工具,大效率

    欢迎大家关注腾讯云技术社区-博客园官方主页,我们将持续在博客园为大家推荐技术精品文章哦~ 作者:姚志锋 一.Hugo插件 -- 打印方法运行时间 首先申明下,此Hugo非 彼Hugo(Hugo是由Go ...

  4. xamarin开发的mac开发小工具集合

    兄弟们我拖控件拖到了mac系统去了, 工具上传到百度网盘,下载地址 链接:https://pan.baidu.com/s/1Q64zoRjE3u66jJnzF8rhww提取码:ljx2 这款工具我是用 ...

  5. javaWeb开发小工具---MailUtils及其单元测试

    本次介绍的是,在javaWeb开发中,我们不免会遇到发送邮件的需求,比如:用户注册账号,需要激活登录,以及服务器定期向会员发送礼品信息等.所以参考有关资料,写了这个MailUtils工具类. 1.Ma ...

  6. javaWeb开发小工具--MyCommonUtils

    MyCommonUtils 参考一些资料,写了这个工具类.在这个工具类中,主要实现了2个方法: 1.生成随机的序列号 uuid(): 2.将Map中的数据封装到javaBean对象中toBean(Ma ...

  7. 分享原创powershell脚本小工具ctracert.ps1

    ----------[脚本介绍]----------- 脚本名称:ctracert.ps1软件名称:灰主牛 跟踪路由 归属地版 V1.0脚本作用:1跟踪路由.2显示归属地.(注意不带显示时间功能)脚本 ...

  8. Qt开发小工具之gif转换器(使用QMovie截取每一帧为QImage,然后用QFile另存为图片文件)

    最近,QQ上好多各种gif表情.每一个都很经典呀..于是我就想把它转换成一张张静态图片...没学过ps.于是写了几行代码.完工.核心代码如下 主要是借助QMovie类.文件读取模式选择QMovie:: ...

  9. CSS便捷开发小工具汇总

    1.Prefix free 可以帮助开发者省去编写各种CSS3属性前缀的工作,只需要在页面中引入prefixfree.js即可. 2. Normalize 是一个CSS Reset工具, 相比传统的R ...

  10. android 开发小工具收集

    http://blog.csdn.net/tikitoo/article/details/51089422

随机推荐

  1. R数据分析:潜在转化分析LTA的做法和解释(一)

    之前给大家写了很多潜在类别分析的教程Mplus教程:如何做潜在类别分析LCA R数据分析:用R语言做潜类别分析LCA Mplus数据分析:潜在类别分析(LCA)流程(详细版) R数据分析:再写潜在类别 ...

  2. 渗透测试-前端加密分析之RSA加密登录(密钥来源本地)

    本文是高级前端加解密与验签实战的第5篇文章,本系列文章实验靶场为Yakit里自带的Vulinbox靶场,本文讲述的是绕过前端RSA加密来爆破登录. 分析 generateKey函数用来生成随机的RSA ...

  3. 【Python】【爬虫】爬虫问题:requests的content和text

    爬虫问题:requests的content和text 通常来说,text获取的是Unicode编码的文本数据,content获取的是byte类型的二进制数据,比如获取图片本身.PDF文件之类的,可以用 ...

  4. Qt音视频开发14-音视频文件保存基类的设计

    一.前言 视频综合应用示例,包括了多种内核,在保存文件这个功能上,需要一个通用的文件保存基类AbstractSaveThread,这个基类定义了是否打印线程消息标志位.直接写入还是排队写入标志位.文件 ...

  5. Qt编写4K/8K大分辨率播放器(8K占用1%CPU)

    一.前言 在经过多种内核的洗礼以后,逐渐对不同内核的不同音视频文件和视频流进行大量的对比测试,比如测试对各种格式的支持性,对各种网络流的支持程度,在同一个地址下占用的CPU/GPU资源比对,最终发现播 ...

  6. Web端即时通讯实践干货:如何让WebSocket断网重连更快速?

    本文作者网易智慧企业web前端开发工程师马莹莹.为了提升内容质量,收录时有修订和改动. 1.引言 在一个完善的即时通讯IM应用中,WebSocket是极其关键的一环,它为基于Web的即时通讯应用提供了 ...

  7. 意外之喜——黑夜 CrossFire!!!

    在日常逛L站时,偶然发现了"友链"功能,机缘巧合下进入了specialhua的博客,又被吸引着点进了其中一篇博客,于是便通过specialhua的博客看到了黑夜的这篇文章,感觉就像 ...

  8. Qml 中实现毛玻璃效果

    [写在前面] 毛玻璃效果(Acrylic Effect)是一种常见的 UI 设计风格,它通过模糊背景并添加透明度和噪声效果,使界面元素看起来像是半透明的磨砂玻璃. 本文将介绍如何使用 Qml 实现这种 ...

  9. 基于.NET8.0实现RabbbitMQ的Publish/Subscribe发布订阅以及死信队列

    [前言] RabbitMQ提供了五种消息模型,分别是简单模型.工作队列模型.发布/订阅模型.路由模型和主题模型.‌‌ 简单模型(Simple)‌:在这种模式下,一个生产者将消息发送到一个队列,只有一个 ...

  10. Cesium中3DTiles使用CustomShader着色器渲染

    加载模型 新版本cesium加载3DTiles代码如下,后续效果只修改CustomShader内内容 //加载楼栋白膜 let tileset try { tileset = await Cesium ...