本文介绍的 VBA 脚本主要实现以下功能:
为什么选择Excel结合VBA与FFmpeg处理图片?
1.多功能集成,一站式解决方案
集成图片的批量选择、调整尺寸、压缩质量以及合并功能,满足在不同场景下的多样化需求,无需切换多个软件工具。
2.自动化操作,省时省力
通过双击Excel中的指定单元格,即可自动执行复杂的图片处理任务,减少手动操作,提升工作效率。
3.灵活定制,适应不同需求
根据具体需求设置目标宽度、高度、压缩质量,并选择合并方式(水平或垂直),灵活应对各种图片处理场景。
4.高质量输出,保证专业水准
借助FFmpeg的强大处理能力,确保处理后的图片质量,无论是用于商业展示还是个人项目,都能达到专业水准。
功能亮点详解
1.批量选择与导入图片路径
通过双击Excel中的A1单元格,弹出文件选择对话框,轻松选择多张图片。选定的图片路径将自动填入A列,便于后续管理与处理。
2.智能获取图片信息
系统自动读取每张图片的格式、分辨率及文件大小,信息一目了然,可以更好地了解和管理图片资源。
3.批量调整图片尺寸与压缩质量
在E 、F列填写目标宽度和高度,G列填写压缩质量(默认值为2)。双击I1单元格,VBA脚本将自动调整所有选定图片的尺寸与质量,处理后的图片将保存在新建的文件夹中。
4.灵活合并图片
-
水平合并:
双击K1单元格,即可将选定的多张图片水平拼接成一张长图,适用于制作横幅或展示图集。
-
垂直合并:
双击J1单元格,即可将选定的多张图片垂直堆叠成一张高图,适用于制作竖版海报或图册。
5.自动化管理,提升效率
处理完成后,所有优化后的图片将自动保存在指定文件夹中,整洁有序,便于后续使用与管理。同时,生成详细的日志文件,方便追踪与排查问题。
下面,我们将逐一解析每个部分的具体实现和功能。
1. 双击单元格事件处理,Worksheet_BeforeDoubleClick 事件
javascript
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
On Error Resume Next
If Target.Address = "$A$1" Then
Call GetSelectedImagePaths
Cancel = True
End If
If Target.Address = "$I$1" Then
Call RunFFCommand
Cancel = True
End If
If Left(Target.Address, 2) = "$I" Or Left(Target.Address, 2) = "$A" Then
If Target.Address <> "$I$1" And Target.Address <> "$A$1" Then
If Target.Value <> "" Then
Cancel = True
ThisWorkbook.FollowHyperlink Address:=Target.Value
End If
End If
End If
If Target.Address = "$J$1" Then
Call VMergeImagesRecursively
Cancel = True
End If
If Target.Address = "$K$1" Then
Call HorizontalImageMerger
Cancel = True
End If
End Sub
功能说明:
javascript
* 触发条件:当用户在工作表中双击某个单元格时,该事件被触发。
* 操作逻辑:
* 双击 $A$1 单元格:调用 GetSelectedImagePaths 子程序,用于选择图片文件路径。
* 双击 $I$1 单元格:调用 RunFFCommand 子程序,执行 FFmpeg 命令。
* 双击 $I 或 $A 列的其他单元格:如果单元格有值,跳转到该值对应的超链接。
* 双击 $J$1 单元格:调用 VMergeImagesRecursively 子程序,执行垂直合并图像操作。
* 双击 $K$1 单元格:调用 HorizontalImageMerger 子程序,执行水平合并图像操作。
通过这种方式,用户可以通过简单的双击操作,快速执行不同的图像处理任务,提高工作效率。
2.FFmpeg进程管理,KillFFmpegIfRunning 子程序
javascript
Sub KillFFmpegIfRunning()
On Error Resume Next
Dim objWMI As Object
Dim objProcess As Object
Dim colProcess As Object
' 获取WMI服务
Set objWMI = GetObject("winmgmts:\\.\root\cimv2")
If objWMI Is Nothing Then
Exit Sub
End If
' 查询进程
Set colProcess = objWMI.ExecQuery("Select * from Win32_Process Where Name = 'ffmpeg.exe'")
If colProcess.Count = 0 Then
Exit Sub
End If
' 遍历并终止所有FFmpeg进程
For Each objProcess In colProcess
objProcess.Terminate
Next
End Sub
功能说明:
javascript
* 目的:在需要时终止所有正在运行的 FFmpeg 进程,释放系统资源。
* 实现方法:
* 利用 WMI(Windows Management Instrumentation)查询系统中所有名为 ffmpeg.exe 的进程。
* 遍历查询结果,逐个终止这些进程。
这种方法确保在执行图像处理任务前,系统中不会有残留的 FFmpeg 进程占用资源,避免潜在的冲突和资源浪费。
3. 工作表格式化,FormatContext 子程序
javascript
Sub FormatContext()
Application.ScreenUpdating = False
Application.DisplayAlerts = False
With Sheets(1).Range("A1:K" & [a65536].End(3).Row)
.Font.Name = "宋体"
.Font.Size = 12
.Font.Underline = xlUnderlineStyleNone
.Font.ColorIndex = xlAutomatic
.Borders.LineStyle = xlContinuous
.Borders.ColorIndex = 0
.Borders.TintAndShade = 0
.Borders.Weight = xlThin
.HorizontalAlignment = xlLeft
.VerticalAlignment = xlCenter
.WrapText = False
.Orientation = 0
.ReadingOrder = xlContext
.MergeCells = False
End With
Sheets(1).Rows("2:10000").RowHeight = 13.25
Sheets(1).Range("B2").Select
Sheets(1).Cells.EntireColumn.AutoFit
Columns("I:I").ColumnWidth = Columns("A:A").ColumnWidth + 20
Application.ScreenUpdating = True
Application.DisplayAlerts = True
End Sub
功能说明:
javascript
* 目的:统一格式化工作表,提高可读性和美观度。
* 操作内容:
* 字体设置:使用"宋体"字体,字号为12,取消下划线,自动颜色。
* 边框设置:为范围内的单元格添加连续线条边框,线条颜色为默认。
* 对齐方式:水平左对齐,垂直居中。
* 文本格式:取消自动换行,取消单元格合并。
* 行高与列宽调整:
* 设置第2行到第10000行的行高为13.25。
* 自动调整所有列的宽度以适应内容。
* 将列 I 的宽度设置为列 A 宽度加20,以容纳更长的内容。
通过该子程序,可以确保生成的工作表具有统一且专业的外观,便于用户查看和操作。
4. 选择并处理图片路径,GetSelectedImagePaths 子程序
javascript
Sub GetSelectedImagePaths()
Dim fd As FileDialog
Dim i As Long
Dim ws As Worksheet
Dim selectedFilePath As Variant
' 设置当前工作表
Set ws = ThisWorkbook.Sheets(1) ' 修改为目标工作表名称或索引
' 创建文件选择对话框
Set fd = Application.FileDialog(msoFileDialogFilePicker)
' 配置对话框属性
With fd
.Title = "选择图片文件"
.Filters.Clear
.Filters.Add "图片文件", "*.jpg; *.jpeg; *.png; *.gif; *.bmp;*.tif;*.tiff;*.ico"
.AllowMultiSelect = True
' 如果用户选择了文件
If .Show = -1 Then
' 初始化起始单元格行
Rows("2:65536").Clear
i = 2
' 遍历选中的文件路径
For Each selectedFilePath In .SelectedItems
' 写入文件路径到A列
ws.Cells(i, 1).Value = selectedFilePath
i = i + 1
Next selectedFilePath
Else
Exit Sub
End If
End With
Call RunGetImageResolutionAsDictionary
Call FormatContext
End Sub
功能说明:
javascript
* 目的:通过文件对话框让用户选择多个图片文件,并将选中的文件路径写入工作表的 A 列。
* 实现步骤:
1.创建文件对话框:设置标题为"选择图片文件",过滤器仅显示常见的图片格式(如 JPG、PNG 等),允许多选。
2.用户选择文件:
* 如果用户选择了文件,清空工作表第2行到最后一行的内容,从第2行开始,将每个选中的文件路径写入 A 列。
* 如果用户取消操作,则退出子程序。
3.后续操作:
* 调用 RunGetImageResolutionAsDictionary 子程序,获取每个图片的分辨率和其他信息。
* 调用 FormatContext 子程序,格式化工作表
此子程序简化了用户选择和记录图片路径的过程,为后续的图像处理打下基础。
5. 获取图片分辨率信息,GetImageResolutionAsDictionary 函数
javascript
Private Declare PtrSafe Function GdipCreateBitmapFromFile Lib "gdiplus" (ByVal fileName As LongPtr, ByRef bitmap As LongPtr) As Long
Private Declare PtrSafe Function GdipGetImageWidth Lib "gdiplus" (ByVal image As LongPtr, ByRef width As Long) As Long
Private Declare PtrSafe Function GdipGetImageHeight Lib "gdiplus" (ByVal image As LongPtr, ByRef height As Long) As Long
Private Declare PtrSafe Function GdipDisposeImage Lib "gdiplus" (ByVal image As LongPtr) As Long
Private Declare PtrSafe Function GdiplusStartup Lib "gdiplus" (ByRef token As LongPtr, ByRef inputbuf As GdiplusStartupInput, ByVal outputbuf As LongPtr) As Long
Private Declare PtrSafe Function GdiplusShutdown Lib "gdiplus" (ByVal token As LongPtr) As Long
Private Type GdiplusStartupInput
GdiplusVersion As Long
DebugEventCallback As LongPtr
SuppressBackgroundThread As Long
SuppressExternalCodecs As Long
End Type
Function GetImageResolutionAsDictionary(ByVal filePath As String) As Object
Dim token As LongPtr
Dim startupInput As GdiplusStartupInput
Dim image As LongPtr
Dim width As Long, height As Long
Dim status As Long
Dim fileExt As String
Dim resultDict As Object ' 用于存储返回值的字典
Dim fso As Object
Dim fileSize As Double
' 创建字典对象
Set resultDict = CreateObject("Scripting.Dictionary")
' 检查文件格式
fileExt = LCase(Right(filePath, Len(filePath) - InStrRev(filePath, ".")))
If fileExt <> "jpg" And fileExt <> "jpeg" And fileExt <> "png" And fileExt <> "gif" And fileExt <> "bmp" And fileExt <> "tif" And fileExt <> "tiff" And fileExt <> "ico" Then
resultDict("Error") = "Unsupported format: " & fileExt
Set GetImageResolutionAsDictionary = resultDict
Exit Function
End If
' 获取文件大小
Set fso = CreateObject("Scripting.FileSystemObject")
If fso.FileExists(filePath) Then
fileSize = fso.GetFile(filePath).Size ' 获取文件大小
Else
resultDict("Error") = "File not found: " & filePath
Set GetImageResolutionAsDictionary = resultDict
Exit Function
End If
' 初始化GDI+
startupInput.GdiplusVersion = 1
status = GdiplusStartup(token, startupInput, 0)
If status <> 0 Then
resultDict("Error") = "Error initializing GDI+"
Set GetImageResolutionAsDictionary = resultDict
Exit Function
End If
' 加载图片
status = GdipCreateBitmapFromFile(StrPtr(filePath), image)
If status <> 0 Then
resultDict("Error") = "Error loading image"
GdiplusShutdown token
Set GetImageResolutionAsDictionary = resultDict
Exit Function
End If
' 获取图片宽高
GdipGetImageWidth image, width
GdipGetImageHeight image, height
' 将结果存储到字典中
resultDict("Format") = fileExt
resultDict("Width") = width
resultDict("Height") = height
resultDict("Size") = fileSize ' 文件大小(字节)
' 释放图片资源
GdipDisposeImage image
' 关闭GDI+
GdiplusShutdown token
' 返回字典
Set GetImageResolutionAsDictionary = resultDict
End Function
功能说明:
javascript
* 目的:获取指定图片文件的格式、宽度、高度和文件大小。
* 实现方法:
1.格式验证:检查文件扩展名是否为支持的图片格式(如 JPG、PNG 等)。
2.文件存在性检查:确认文件是否存在。
3.GDI+ 初始化:利用 GDI+ API 加载图片文件。
4.获取图像信息:
* 获取图片的宽度和高度。
* 获取文件大小(以字节为单位)。
5.资源释放:释放加载的图片资源,关闭 GDI+。
6.结果返回:将获取的信息存储在字典对象中返回。
该函数通过直接调用 GDI+ API,能够高效准确地获取图片的详细信息,为后续的数据处理提供支持。
5.1 RunGetImageResolutionAsDictionary 子程序
javascript
Sub RunGetImageResolutionAsDictionary()
Dim resolutionDict As Object
Dim filePath As String
Dim lastRow As Long
Dim rng1 As Range
Dim ws As Worksheet
' 设置目标工作表
Set ws = ThisWorkbook.Sheets(1)
' 确定最后一行
lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
' 遍历A列中的文件路径,从第2行开始
For Each rng1 In ws.Range("A2:A" & lastRow)
If Len(rng1.Value) > 0 Then ' 如果单元格不为空
filePath = rng1.Value
Set resolutionDict = GetImageResolutionAsDictionary(filePath) ' 调用函数获取分辨率信息
If resolutionDict.Exists("Error") Then
' 如果出错,将错误信息写入相邻列
'rng1.Offset(0, 1).Value = resolutionDict("Error")
rng1.Interior.Color = RGB(255, 0, 0)
rng1.Offset(0, 4).Value = -1
rng1.Offset(0, 5).Value = -1
rng1.Offset(0, 6).Value = 2
rng1.Offset(0, 7).Value = LCase(Right(rng1.Value, Len(rng1.Value) - InStrRev(rng1.Value, ".")))
Else
' 如果成功,写入格式、宽度和高度
'rng1.Offset(0, 1).Value = resolutionDict("Format")
rng1.Offset(0, 1).Value = resolutionDict("Width")
rng1.Offset(0, 2).Value = resolutionDict("Height")
rng1.Offset(0, 3).Value = Format(resolutionDict("Size") / 1048576, "0.00")
rng1.Offset(0, 4).Value = -1
rng1.Offset(0, 5).Value = -1
rng1.Offset(0, 6).Value = 2
rng1.Offset(0, 7).Value = resolutionDict("Format")
End If
End If
Next rng1
End Sub
功能说明:
javascript
* 目的:遍历工作表 A 列中的图片文件路径,调用 GetImageResolutionAsDictionary 函数获取每个图片的详细信息,并将结果写入相邻的列中。
* 实现步骤:
1.遍历 A 列:从第2行开始,遍历所有非空的单元格。
2.获取图片信息:调用 GetImageResolutionAsDictionary 函数,获取每个图片的格式、宽度、高度和大小。
3.结果处理:
* 出错处理:如果获取信息时发生错误(如不支持的格式或文件不存在),将单元格背景颜色设置为红色,并在相关列中标记错误信息。
* 成功处理:将图片的宽度、高度、大小(以MB为单位)和格式写入相应的列中,同时设置其他相关信息。
通过该子程序,用户可以直观地在工作表中查看每个图片的详细信息,便于后续的筛选和处理。
6. 水平合并图像,HorizontalImageMerger 子程序
javascript
Sub HorizontalImageMerger()
Dim ffmpegExecutablePath As String
Dim finalMergedImagePath As String
Dim imagePaths As Collection
Dim currentCell As Range
Dim userSelectedRange As Range
Dim temporaryFolderPath As String
Dim tempFileNamePrefix As String
Dim imagesPerBatch As Long
Dim mergedImageFiles As Collection
Dim maximumIterations As Long
Dim iterationCount As Long
Dim logFilePath As String
Dim logFileStream As Object
Dim uniqueMergeCounter As Long
' Set FFmpeg path
ffmpegExecutablePath = ThisWorkbook.Path & "\bin\ffmpeg.exe" ' Modify to the full path of FFmpeg if needed
' Verify FFmpeg exists
If Dir(ffmpegExecutablePath) = "" Then
MsgBox "FFmpeg executable not found: " & ffmpegExecutablePath, vbCritical
Exit Sub
End If
' Set the final output image path with timestamp
finalMergedImagePath = ThisWorkbook.Path & "\Successful\" & Format(Now, "yyyy_mm_dd_hhmmss") & ".jpg"
If Dir(ThisWorkbook.Path & "\Successful\", vbDirectory) = "" Then
MkDir ThisWorkbook.Path & "\Successful\"
End If
' Set temporary folder path
temporaryFolderPath = ThisWorkbook.Path & "\temp_ffmpeg_merge"
If Dir(temporaryFolderPath, vbDirectory) = "" Then
MkDir temporaryFolderPath
End If
' Set temporary file prefix
tempFileNamePrefix = "temp_merge_"
' Set the number of images to merge per batch
imagesPerBatch = 4 ' Adjust as needed to reduce the number of iterations
' Initialize the collection to store image paths
Set imagePaths = New Collection
' Initialize log file
logFilePath = ThisWorkbook.Path & "\merge_log.txt"
Set logFileStream = CreateObject("Scripting.FileSystemObject").CreateTextFile(logFilePath, True)
' Initialize merge counter
uniqueMergeCounter = 1
' Get the user-selected cell range
On Error Resume Next
Set userSelectedRange = Application.InputBox("Please select the cell range containing image paths (Column I, starting from I2):", "Select Image Paths", Type:=8)
On Error GoTo 0
If userSelectedRange Is Nothing Then
MsgBox "No cells selected. Operation canceled.", vbExclamation
logFileStream.WriteLine "No cells selected. Operation canceled."
logFileStream.Close
Exit Sub
End If
' Collect image paths
For Each currentCell In userSelectedRange
If currentCell.Column = 9 Then ' Column I is the 9th column
If Trim(currentCell.Value) <> "" Then
imagePaths.Add currentCell.Value
End If
End If
Next currentCell
If imagePaths.Count < 1 Then
MsgBox "No image paths found. Please ensure the selected cells contain valid image paths.", vbExclamation
logFileStream.WriteLine "No image paths found."
logFileStream.Close
Exit Sub
End If
' Check if all image files exist
Dim index As Long
For index = 1 To imagePaths.Count
If Dir(imagePaths(index)) = "" Then
MsgBox "Image file not found: " & imagePaths(index), vbCritical
logFileStream.WriteLine "Image file not found: " & imagePaths(index)
logFileStream.Close
Exit Sub
End If
Next index
' Log start time and total image count
logFileStream.WriteLine "Start merging images: " & Now
logFileStream.WriteLine "Total number of images: " & imagePaths.Count
' Start initial merging
Set mergedImageFiles = BatchMergeImages(imagePaths, ffmpegExecutablePath, temporaryFolderPath, tempFileNamePrefix, imagesPerBatch, logFileStream, "hstack", uniqueMergeCounter)
' Set maximum number of iterations to prevent infinite loops
maximumIterations = 10
iterationCount = 0
' Recursively merge until only one file remains or maximum iterations are reached
Do While mergedImageFiles.Count > 1 And iterationCount < maximumIterations
Set mergedImageFiles = BatchMergeImages(mergedImageFiles, ffmpegExecutablePath, temporaryFolderPath, tempFileNamePrefix, imagesPerBatch, logFileStream, "hstack", uniqueMergeCounter)
iterationCount = iterationCount + 1
logFileStream.WriteLine "Iteration: " & iterationCount & ", Remaining files: " & mergedImageFiles.Count
Loop
' Check if successfully merged into one file
If mergedImageFiles.Count = 1 Then
' Convert the final merged PNG to JPG
Dim finalPngFile As String
finalPngFile = mergedImageFiles(1)
' Build FFmpeg command to convert PNG to JPG
Dim conversionCommand As String
conversionCommand = """" & ffmpegExecutablePath & """ -i """ & finalPngFile & """ -q:v 2 """ & finalMergedImagePath & """"
logFileStream.WriteLine "Converting PNG to JPG: " & conversionCommand
' Execute conversion command
If Not RunFFmpegCommand(conversionCommand) Then
MsgBox "Failed to convert final PNG to JPG.", vbCritical
logFileStream.WriteLine "Failed to convert final PNG to JPG: " & conversionCommand
logFileStream.Close
Exit Sub
End If
' Delete the final PNG file
If Dir(finalPngFile) <> "" Then
Kill finalPngFile
logFileStream.WriteLine "Deleted temporary PNG file: " & finalPngFile
End If
logFileStream.WriteLine "Successfully merged images into: " & finalMergedImagePath
Else
MsgBox "Merging process did not complete. There may be an issue with the merging steps or it exceeded the maximum number of iterations.", vbCritical
logFileStream.WriteLine "Merging process did not complete. Final file count: " & mergedImageFiles.Count
logFileStream.Close
Exit Sub
End If
' Delete the temporary folder and its contents
RemoveTemporaryFolder temporaryFolderPath
logFileStream.WriteLine "Deleted temporary folder: " & temporaryFolderPath
' Log end time
logFileStream.WriteLine "Image merging completed: " & Now
logFileStream.Close
' Notify the user
If Dir(finalMergedImagePath) <> "" Then
MsgBox "Images have been successfully merged and saved as: " & finalMergedImagePath, vbInformation
Else
MsgBox "Image merging failed. Please check the FFmpeg commands and ensure all image paths are valid.", vbCritical
End If
End Sub
功能说明:
javascript
* 目的:使用 FFmpeg 工具将选定的多张图片水平合并为一张图片。
* 实现步骤:
1.FFmpeg 路径设置:指定 FFmpeg 可执行文件的位置,默认位于工作簿所在路径的 bin 文件夹中。
2.输出路径设置:设置合并后图片的保存路径,文件名包含时间戳。
3.临时文件夹创建:用于存放中间合并生成的临时文件。
4.用户选择图片范围:弹出对话框让用户选择包含图片路径的单元格区域(默认列为 I 列)。
5.图片存在性检查:确保所有选定的图片文件都存在。
6.批量合并:调用 BatchMergeImages 函数,将图片分批次合并,直到最终只剩一张合并后的图片。
7.PNG 转 JPG:将最终的 PNG 格式图片转换为 JPG 格式。
8.清理临时文件:删除临时文件夹及其内容。
9.日志记录:记录合并过程中的详细信息,便于后续排查问题。
6.1 BatchMergeImages 函数
javascript
Function BatchMergeImages(inputFiles As Collection, ffmpegPath As String, tempFolder As String, tempPrefix As String, batchSize As Long, logStream As Object, mergeType As String, ByRef mergeCounter As Long) As Collection
Dim outputFiles As New Collection
Dim i As Long, batchNumber As Long
Dim currentBatch As New Collection
Dim tempOutputPath As String
Dim ffmpegCommand As String
Dim j As Long
batchNumber = 1
For i = 1 To inputFiles.Count
currentBatch.Add inputFiles(i)
If currentBatch.Count = batchSize Or i = inputFiles.Count Then
If currentBatch.Count = 1 Then
' Only one image, no need to merge, directly add to output collection
outputFiles.Add currentBatch(1)
logStream.WriteLine "Batch " & batchNumber & ": Single image, skipping merge: " & currentBatch(1)
Else
' Set temporary output file name as PNG to ensure uniqueness
tempOutputPath = tempFolder & "\" & tempPrefix & mergeCounter & ".png"
' Build FFmpeg command
ffmpegCommand = """" & ffmpegPath & """"
For j = 1 To currentBatch.Count
ffmpegCommand = ffmpegCommand & " -i """ & currentBatch(j) & """"
Next j
ffmpegCommand = ffmpegCommand & " -filter_complex """ & mergeType & "=inputs=" & currentBatch.Count & """ """ & tempOutputPath & """"
' Log the command
logStream.WriteLine "Batch " & batchNumber & ": Executing FFmpeg command: " & ffmpegCommand
' Execute FFmpeg command
If Not RunFFmpegCommand(ffmpegCommand) Then
MsgBox "Failed to execute FFmpeg command:" & vbCrLf & ffmpegCommand, vbCritical
logStream.WriteLine "Failed to execute FFmpeg command: " & ffmpegCommand
Exit Function
Else
logStream.WriteLine "Batch " & batchNumber & ": Successfully merged into " & tempOutputPath
outputFiles.Add tempOutputPath
mergeCounter = mergeCounter + 1
End If
' Increment batch counter
batchNumber = batchNumber + 1
End If
' Reset the current batch
Set currentBatch = New Collection
End If
Next i
Set BatchMergeImages = outputFiles
End Function
功能说明:
javascript
* 目的:将输入的图片文件集合分批次进行合并,支持水平(hstack)或垂直(vstack)合并。
* 实现方法:
1.批次处理:根据设定的 batchSize(每批次合并的图片数量),将输入文件集合分成若干批次。
2.FFmpeg 命令构建:为每个批次构建相应的 FFmpeg 合并命令。
3.执行命令:调用 RunFFmpegCommand 函数,执行合并操作。
4.结果收集:将每个批次合并后的临时文件路径添加到输出集合中,供后续合并使用。
通过分批次合并,可以有效管理大规模图片的合并过程,避免一次性处理过多图片导致的资源消耗过大或失败。
6.2 RunFFmpegCommand 函数
javascript
Function RunFFmpegCommand(command As String) As Boolean
On Error GoTo ErrorHandler
Dim shell As Object
Set shell = CreateObject("WScript.Shell")
' Use Run method to execute FFmpeg command
' Parameters:
' 0 - Hide window
' True - Wait for command to complete
Dim exitCode As Long
exitCode = shell.run(command, 0, True)
' Check if command executed successfully
If exitCode = 0 Then
RunFFmpegCommand = True
Else
RunFFmpegCommand = False
End If
Exit Function
ErrorHandler:
RunFFmpegCommand = False
End Function
功能说明:
javascript
* 目的:执行构建好的 FFmpeg 命令,并判断执行是否成功。
* 实现方法:
1.创建 Shell 对象:利用 WScript.Shell 对象执行命令行指令。
2.执行命令:使用 Run 方法执行 FFmpeg 命令,参数设置为隐藏窗口并等待命令完成。
3.结果判断:根据返回的 exitCode 判断命令是否成功执行(0 表示成功)。
4.错误处理:如果执行过程中发生错误,返回 False。
该函数确保 FFmpeg 命令的可靠执行,并为合并过程提供反馈。
6.3 RemoveTemporaryFolder 子程序
javascript
' Sub to delete temporary folder and its contents
Sub RemoveTemporaryFolder(folderPath As String)
On Error Resume Next
Dim fso As Object
Set fso = CreateObject("Scripting.FileSystemObject")
If fso.FolderExists(folderPath) Then
fso.DeleteFolder folderPath, True
End If
End Sub
功能说明:
javascript
* 目的:删除指定的临时文件夹及其内容,进行清理操作。
* 实现方法:
* 使用 Scripting.FileSystemObject 对象检查文件夹是否存在,如果存在则删除整个文件夹及其内部所有文件。
7. 垂直合并图像,VMergeImagesRecursively 子程序
javascript
Sub VMergeImagesRecursively()
Dim ffmpegPath As String
Dim finalOutput As String
Dim imgPaths As Collection
Dim cell As Range
Dim selectedRange As Range
Dim tempFolder As String
Dim tempFilePrefix As String
Dim batchSize As Long
Dim mergedFiles As Collection
Dim maxIterations As Long
Dim currentIteration As Long
Dim logFile As String
Dim logStream As Object
Dim mergeCounter As Long
' Set FFmpeg path
ffmpegPath = ThisWorkbook.Path & "\bin\ffmpeg.exe" ' Modify to the full path of FFmpeg if needed
' Verify FFmpeg exists
If Dir(ffmpegPath) = "" Then
MsgBox "FFmpeg executable not found: " & ffmpegPath, vbCritical
Exit Sub
End If
' Set the final output image path
finalOutput = ThisWorkbook.Path & "\Successful\" & Format(Now, "yyyy_mm_dd_hhmmss") & ".jpg"
If Dir(ThisWorkbook.Path & "\Successful\", vbDirectory) = "" Then
MkDir ThisWorkbook.Path & "\Successful\"
End If
' Set temporary folder path
tempFolder = ThisWorkbook.Path & "\temp_ffmpeg_merge"
If Dir(tempFolder, vbDirectory) = "" Then
MkDir tempFolder
End If
' Set temporary file prefix
tempFilePrefix = "temp_merge_"
' Set the number of images to merge per batch
batchSize = 4 ' Adjust as needed to reduce the number of iterations
' Initialize the collection to store image paths
Set imgPaths = New Collection
' Initialize log file
logFile = ThisWorkbook.Path & "\merge_log.txt"
Set logStream = CreateObject("Scripting.FileSystemObject").CreateTextFile(logFile, True)
' Initialize merge counter
mergeCounter = 1
' Get the user-selected cell range
On Error Resume Next
Set selectedRange = Application.InputBox("Please select the cell range containing image paths (Column I, starting from I2):", "Select Image Paths", Type:=8)
On Error GoTo 0
If selectedRange Is Nothing Then
MsgBox "No cells selected. Operation canceled.", vbExclamation
logStream.WriteLine "No cells selected. Operation canceled."
logStream.Close
Exit Sub
End If
' Collect image paths
For Each cell In selectedRange
If cell.Column = 9 Then ' Column I is the 9th column
If Trim(cell.Value) <> "" Then
imgPaths.Add cell.Value
End If
End If
Next cell
If imgPaths.Count < 1 Then
MsgBox "No image paths found. Please ensure the selected cells contain valid image paths.", vbExclamation
logStream.WriteLine "No image paths found."
logStream.Close
Exit Sub
End If
' Check if all image files exist
Dim i As Long
For i = 1 To imgPaths.Count
If Dir(imgPaths(i)) = "" Then
MsgBox "Image file not found: " & imgPaths(i), vbCritical
logStream.WriteLine "Image file not found: " & imgPaths(i)
logStream.Close
Exit Sub
End If
Next i
' Log start time and total image count
logStream.WriteLine "Start merging images: " & Now
logStream.WriteLine "Total number of images: " & imgPaths.Count
' Start initial merging
Set mergedFiles = MergeInBatches(imgPaths, ffmpegPath, tempFolder, tempFilePrefix, batchSize, logStream, "vstack", mergeCounter)
' Set maximum number of iterations to prevent infinite loops
maxIterations = 10
currentIteration = 0
' Recursively merge until only one file remains or maximum iterations are reached
Do While mergedFiles.Count > 1 And currentIteration < maxIterations
Set mergedFiles = MergeInBatches(mergedFiles, ffmpegPath, tempFolder, tempFilePrefix, batchSize, logStream, "vstack", mergeCounter)
currentIteration = currentIteration + 1
logStream.WriteLine "Iteration: " & currentIteration & ", Remaining files: " & mergedFiles.Count
Loop
' Check if successfully merged into one file
If mergedFiles.Count = 1 Then
' Convert the final merged PNG to JPG
Dim pngFile As String
pngFile = mergedFiles(1)
' Build FFmpeg command to convert PNG to JPG
Dim convertCmd As String
convertCmd = """" & ffmpegPath & """ -i """ & pngFile & """ -q:v 2 """ & finalOutput & """"
logStream.WriteLine "Converting PNG to JPG: " & convertCmd
' Execute conversion command
If Not ExecuteFFMPEG(convertCmd) Then
MsgBox "Failed to convert final PNG to JPG.", vbCritical
logStream.WriteLine "Failed to convert final PNG to JPG: " & convertCmd
logStream.Close
Exit Sub
End If
' Delete the final PNG file
If Dir(pngFile) <> "" Then
Kill pngFile
logStream.WriteLine "Deleted temporary PNG file: " & pngFile
End If
logStream.WriteLine "Successfully merged images into: " & finalOutput
Else
MsgBox "Merging process did not complete. There may be an issue with the merging steps or it exceeded the maximum number of iterations.", vbCritical
logStream.WriteLine "Merging process did not complete. Final file count: " & mergedFiles.Count
logStream.Close
Exit Sub
End If
' Delete the temporary folder and its contents
DeleteFolder tempFolder
logStream.WriteLine "Deleted temporary folder: " & tempFolder
' Log end time
logStream.WriteLine "Image merging completed: " & Now
logStream.Close
' Notify the user
If Dir(finalOutput) <> "" Then
MsgBox "Images have been successfully merged and saved as: " & finalOutput, vbInformation
Else
MsgBox "Image merging failed. Please check the FFmpeg commands and ensure all image paths are valid.", vbCritical
End If
End Sub
功能说明:
javascript
* 目的:使用 FFmpeg 工具将选定的多张图片垂直合并为一张图片。
* 实现步骤:
1.FFmpeg 路径设置:指定 FFmpeg 可执行文件的位置。
2.输出路径设置:设置合并后图片的保存路径,文件名包含时间戳。
3.临时文件夹创建:用于存放中间合并生成的临时文件。
4.用户选择图片范围:弹出对话框让用户选择包含图片路径的单元格区域(默认列为 I 列)。
5.图片存在性检查:确保所有选定的图片文件都存在。
6.批量合并:调用 MergeInBatches 函数,将图片分批次合并,直到最终只剩一张合并后的图片。
7.PNG 转 JPG:将最终的 PNG 格式图片转换为 JPG 格式。
8.清理临时文件:删除临时文件夹及其内容。
9.日志记录:记录合并过程中的详细信息,便于后续排查问题。
7.1 MergeInBatches 函数
javascript
Function MergeInBatches(inputFiles As Collection, ffmpegPath As String, tempFolder As String, tempFilePrefix As String, batchSize As Long, logStream As Object, mergeType As String, ByRef mergeCounter As Long) As Collection
Dim outputFiles As New Collection
Dim i As Long, j As Long
Dim batch As New Collection
Dim tempOutput As String
Dim cmd As String
Dim k As Long
j = 1
For i = 1 To inputFiles.Count
batch.Add inputFiles(i)
If batch.Count = batchSize Or i = inputFiles.Count Then
If batch.Count = 1 Then
' Only one image, no need to merge, directly add to output collection
outputFiles.Add batch(1)
logStream.WriteLine "Batch " & j & ": Single image, skipping merge: " & batch(1)
Else
' Set temporary output file name as PNG to ensure uniqueness
tempOutput = tempFolder & "\" & tempFilePrefix & mergeCounter & ".png"
' Build FFmpeg command
cmd = """" & ffmpegPath & """"
For k = 1 To batch.Count
cmd = cmd & " -i """ & batch(k) & """"
Next k
cmd = cmd & " -filter_complex """ & mergeType & "=inputs=" & batch.Count & """ """ & tempOutput & """"
' Log the command
logStream.WriteLine "Batch " & j & ": Executing FFmpeg command: " & cmd
' Execute FFmpeg command
If Not ExecuteFFMPEG(cmd) Then
MsgBox "Failed to execute FFmpeg command:" & vbCrLf & cmd, vbCritical
logStream.WriteLine "Failed to execute FFmpeg command: " & cmd
Exit Function
Else
logStream.WriteLine "Batch " & j & ": Successfully merged into " & tempOutput
outputFiles.Add tempOutput
mergeCounter = mergeCounter + 1
End If
' Increment batch counter
j = j + 1
End If
' Reset the current batch
Set batch = New Collection
End If
Next i
Set MergeInBatches = outputFiles
End Function
功能说明:
javascript
* 目的:将输入的图片文件集合分批次进行合并,支持垂直(vstack)或水平(hstack)合并。
* 实现方法:
1.批次处理:根据设定的 batchSize(每批次合并的图片数量),将输入文件集合分成若干批次。
2.FFmpeg 命令构建:为每个批次构建相应的 FFmpeg 合并命令。
3.执行命令:调用 ExecuteFFMPEG 函数,执行合并操作。
4.结果收集:将每个批次合并后的临时文件路径添加到输出集合中,供后续合并使用。
7.2 ExecuteFFMPEG 函数
javascript
Function ExecuteFFMPEG(cmd As String) As Boolean
On Error GoTo ErrorHandler
Dim wsh As Object
Set wsh = CreateObject("WScript.Shell")
' Use Run method to execute FFmpeg command
' Parameters:
' 0 - Hide window
' True - Wait for command to complete
Dim exitCode As Long
exitCode = wsh.run(cmd, 0, True)
' Check if command executed successfully
If exitCode = 0 Then
ExecuteFFMPEG = True
Else
ExecuteFFMPEG = False
End If
Exit Function
ErrorHandler:
ExecuteFFMPEG = False
End Function
功能说明:
javascript
*与 RunFFmpegCommand 函数类似,用于执行 FFmpeg 命令并判断执行结果。
7.3 DeleteFolder 子程序
javascript
Sub DeleteFolder(folderPath As String)
On Error Resume Next
Dim fso As Object
Set fso = CreateObject("Scripting.FileSystemObject")
If fso.FolderExists(folderPath) Then
fso.DeleteFolder folderPath, True
End If
End Sub
功能说明:
javascript
* 目的:删除指定的文件夹及其内容,类似于 RemoveTemporaryFolder 子程序。
8. 执行 FFmpeg 命令
javascript
Sub RunFFCommand()
Dim ffmpegPath As String
Dim inputFilePath As String
Dim outputFilePath As String
Dim cmdCommand As String
Dim newFolderPath As String
Dim fileName As String
Dim basePath As String
Dim fullFilePath As String
Dim fso As Object
Dim wsh As Object
Dim execObj As Object
Set fso = CreateObject("Scripting.FileSystemObject")
Set wsh = CreateObject("WScript.Shell")
fullFilePath = Cells(2, 1).Value
basePath = Left(fullFilePath, InStrRev(fullFilePath, "\") - 1) & "\"
newFolderPath = basePath & Format(Now, "YYYY_MM_DD_HHMMSS") & "\"
If Not fso.FolderExists(newFolderPath) Then
' 创建新文件夹
fso.CreateFolder newFolderPath
End If
Dim i As Long
Dim w As Long
Dim h As Long
Dim c As Long
ffmpegPath = ThisWorkbook.Path & "\bin\ffmpeg.exe"
For i = 2 To [a65536].End(3).Row
If Len(Cells(i, "B")) = 0 And Len(Cells(i, "E")) = 0 Then
w = 300
End If
If Len(Cells(i, "C")) = 0 And Len(Cells(i, "F")) = 0 Then
h = 300
End If
If Len(Cells(i, "G")) = 0 Then
c = 2
End If
'fileName = Mid(Cells(i, "A").Value, InStrRev(Cells(i, "A").Value, "\") + 1)
fileName = Left(Mid(Cells(i, "A").Value, InStrRev(Cells(i, "A").Value, "\") + 1), InStrRev(Mid(Cells(i, "A").Value, InStrRev(Cells(i, "A").Value, "\") + 1), ".")) & Cells(i, "H").Value
'Debug.Print Left(Mid(Cells(i, "A").Value, InStrRev(Cells(i, "A").Value, "\") + 1), InStrRev(Mid(Cells(i, "A").Value, InStrRev(Cells(i, "A").Value, "\") + 1), ".")) & Cells(i, "H").Value
Cells(i, "I").Value = newFolderPath & fileName
Cells(i, "I").Interior.Color = RGB(146, 208, 80)
inputFilePath = Cells(i, "A").Value
outputFilePath = newFolderPath & fileName
If Len(Cells(i, "E").Value) > 0 Then
w = Cells(i, "E").Value
Else
If Len(Cells(i, "B").Value) > 0 Then
w = Cells(i, "B").Value
End If
End If
If Len(Cells(i, "F").Value) > 0 Then
h = Cells(i, "F").Value
Else
If Len(Cells(i, "C").Value) > 0 Then
h = Cells(i, "C").Value
End If
End If
If Len(Cells(i, "G").Value) > 0 Then
c = Cells(i, "G").Value
End If
cmdCommand = ffmpegPath & " -i """ & inputFilePath & """ -q:v " & c & " -vf scale=" & w & ":" & h & " """ & outputFilePath & """"
'Shell "cmd.exe /c " & cmdCommand, vbHide
wsh.run "cmd.exe /c " & cmdCommand, 0, True
Next
KillFFmpegIfRunning
End Sub
功能说明:
javascript
* 目的:根据工作表中设定的参数,使用 FFmpeg 对图片进行缩放和质量调整。
* 实现步骤:
1.FFmpeg 路径设置:指定 FFmpeg 可执行文件的位置。
2.新文件夹创建:根据当前时间戳创建一个新的文件夹,用于存放处理后的图片。
3.遍历图片路径:从工作表的 A 列遍历所有图片路径,根据 B、C、E、F、G 列的值设置缩放参数和质量。
E列和F列可以重新给图片设置宽高,默认-1(保持原来的宽高)
G压缩效果:
2-5 几乎无损压缩,视觉上与原图几乎无差别 大文件
6-15 高质量压缩,适合大多数场景 较小文件
16-25 中等质量,适合对大小要求严格的情况 小文件
26-31 低质量,压缩率极高,但图像质量明显下降 非常小的文件
4.构建 FFmpeg 命令:根据设定的参数,构建相应的 FFmpeg 命令,用于缩放图片和调整质量。
5.执行命令:调用 WScript.Shell 对象执行 FFmpeg 命令,隐藏命令行窗口并等待执行完成。
6.终止 FFmpeg 进程:调用 KillFFmpegIfRunning 子程序,确保所有 FFmpeg 进程都被终止。
通过该子程序,用户可以根据需要对图片进行批量缩放和质量调整,生成符合需求的图像文件。
总结
本文详细解析了一段功能强大的Excel VBA脚本,涵盖了图片路径选择、信息获取、格式化、水平和垂直合并以及进程管理等多项功能。通过结合Excel的强大数据处理能力和FFmpeg的高效图像处理能力,该脚本能够显著提升图像处理的效率和准确性。
关键技术点:
-
VBA 事件驱动编程:
通过双击事件触发特定操作,增强用户交互体验。
-
外部工具集成:
利用FFmpeg实现高效的图像合并和处理,扩展了VBA的功能。
-
GDI+ API 调用:
通过API获取图片的详细信息,展示了VBA与系统底层的交互能力。
-
错误处理与日志记录:
确保在执行过程中出现问题时,能够及时反馈并记录日志,便于排查。
使用建议:
-
环境准备:
确保FFmpeg已正确安装,并将其可执行文件放置在脚本指定的路径中(如
bin
文件夹)。 -
权限设置:
在执行脚本前,确保Excel拥有必要的权限,以访问文件系统和执行外部命令。
-
备份数据:
在批量处理图片前,建议备份原始数据,防止误操作导致数据丢失。
通过合理应用和调整,可以根据自身需求进一步扩展和优化该脚本,实现更加复杂和定制化的图像处理任务。
PS: 如有需求,可在评论区留言!!!
各位看官,创作不易,记得动动发财的小手点个三连!!!