Array.Add () and += in PowerShell

博客讨论了在Windows PowerShell中创建新数组并使用Add()方法时会报错的问题。原因是数组大小固定,无法直接添加元素。还给出了两种解决办法,一种是创建包含原元素和新增元素的新数组并覆盖原数组。
$newArray = @()
$newArray.Add("Hello")

  

If I create a new array, and using the method Add(). Windows PowerShell will tell me :

Exception calling "Add" with "1" argument(s): "Collection was of a fixed size."

 

Reason: 

When you use the $array.Add() method, you're trying to add the element into the array. An array is a collection of fixed size, so you will recieve an error.

 

So, what should I do ?

 

Solution 1:

$successfulArray = New-Object System.Collections.Generic.List[System.Object]

$successfulArray.Add("Hello")
$successfulArray.Add("World")

# When you need array, you can transfer like:
$successfulArray.ToArray()

  

Also a more simple solution 2:

$easyArray = @()
$easyArray += "Hello"
$easyArray += "World"

  PS creates a NEW array with the same elements as $array+ the one(s) you're adding, and then it overwrites the original.

转载于:https://www.cnblogs.com/wushuaiyi/p/5687682.html

PS C:\Users\Administrator> Install-Module -Name Pester -Force -SkipPublisherCheck >> 需需要要使使用用 NuGet 提提供供程程序序来来继继续续操操作作 PowerShellGet 需需要要使使用用 NuGet 提提供供程程序序““2.8.5.201””或或更更高高版版本本来来与与基基于于 NuGet 的的存存储储库库交交互互。。必必须须在在““C:\Program Files\PackageManagement\ProviderAssemblies””或或““C:\Users\Administrator\AppData\Local\PackageManagement\ProviderAssemblies”” 中提供 NuGet 提供程序。也可以通过运行 'Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force' 安装 NuGet 提供程序。是否要让 PowerShellGet 立即安装并导入 NuGet 提供程序? [Y] 是(Y) [N] 否(N) [S] 暂停(S) [?] 帮助 (默认值为“Y”): y PS C:\Users\Administrator> PS C:\Users\Administrator> PS C:\Users\Administrator> # 执行所有测试 >> Invoke-Pester -Path "E:\CurlTools\Modules\Tests\CurlTools.Tests.ps1" >> >> # 输出示例 >> Describing 模块加载测试 >> [+] 应成功导入模块 102ms >> [+] 应导出Get-CurlPath函数 41ms >> [+] 应正确检测curl.exe路径 35ms >> >> Describing 核心功能测试 >> [+] Get-CurlPath返回有效的可执行文件路径 52ms >> [+] Get-CurlVersion返回版本信息 78ms >> [+] Invoke-SecureDownload成功下载文件 1.2s >> [+] 优雅处理无效URL 45ms >> >> Describing 安全策略测试 >> [+] 应拒绝未授权域名 32ms >> [+] 应允许github.com下载 890ms >> 所在位置 行:6 字符: 4 + [+] 应成功导入模块 102ms + ~ "[" 后面缺少类型名称。 所在位置 行:7 字符: 4 + [+] 应导出Get-CurlPath函数 41ms + ~ "[" 后面缺少类型名称。 所在位置 行:8 字符: 4 + [+] 应正确检测curl.exe路径 35ms + ~ "[" 后面缺少类型名称。 所在位置 行:11 字符: 4 + [+] Get-CurlPath返回有效的可执行文件路径 52ms + ~ "[" 后面缺少类型名称。 所在位置 行:12 字符: 4 + [+] Get-CurlVersion返回版本信息 78ms + ~ "[" 后面缺少类型名称。 所在位置 行:13 字符: 4 + [+] Invoke-SecureDownload成功下载文件 1.2s + ~ "[" 后面缺少类型名称。 所在位置 行:14 字符: 4 + [+] 优雅处理无效URL 45ms + ~ "[" 后面缺少类型名称。 所在位置 行:17 字符: 4 + [+] 应拒绝未授权域名 32ms + ~ "[" 后面缺少类型名称。 所在位置 行:18 字符: 4 + [+] 应允许github.com下载 890ms + ~ "[" 后面缺少类型名称。 + CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException + FullyQualifiedErrorId : MissingTypename PS C:\Users\Administrator> name: CurlTools CI >> >> on: [push, pull_request] >> >> jobs: >> test: >> runs-on: windows-latest >> steps: >> - uses: actions/checkout@v2 >> - name: Setup PowerShell >> uses: actions/setup-powershell@v1 >> with: >> pwsh: true >> - name: Run Pester tests >> run: | >> Install-Module Pester -Force -AllowClobber >> Invoke-Pester -Path .\Modules\Tests\ -OutputFile TestResults.xml -OutputFormat NUnitXml >> - name: Publish test results >> uses: actions/upload-artifact@v2 >> with: >> name: TestResults >> path: TestResults.xml >> 所在位置 行:9 字符: 6 + - uses: actions/checkout@v2 + ~ 一元运算符“-”后面缺少表达式。 所在位置 行:9 字符: 7 + - uses: actions/checkout@v2 + ~~~~~ 表达式或语句中包含意外的标记“uses:”。 所在位置 行:10 字符: 6 + - name: Setup PowerShell + ~ 一元运算符“-”后面缺少表达式。 所在位置 行:10 字符: 7 + - name: Setup PowerShell + ~~~~~ 表达式或语句中包含意外的标记“name:”。 所在位置 行:14 字符: 6 + - name: Run Pester tests + ~ 一元运算符“-”后面缺少表达式。 所在位置 行:14 字符: 7 + - name: Run Pester tests + ~~~~~ 表达式或语句中包含意外的标记“name:”。 所在位置 行:18 字符: 6 + - name: Publish test results + ~ 一元运算符“-”后面缺少表达式。 所在位置 行:18 字符: 7 + - name: Publish test results + ~~~~~ 表达式或语句中包含意外的标记“name:”。 + CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException + FullyQualifiedErrorId : MissingExpressionAfterOperator PS C:\Users\Administrator> It "应处理大文件下载" { >> # 测试>1GB文件下载 >> } >> >> It "应验证文件哈希" { >> # 测试哈希校验功能 >> } >> >> It "应限制下载速度" { >> # 测试限速功能 >> } >> 不能对 Null 值表达式调用方法。 所在位置 C:\Program Files\WindowsPowerShell\Modules\Pester\5.7.1\Pester.psm1:1026 字符: 35 + ... ath = @(<# Get full name #> $history = $state.Stack.ToArray(); [Array ... + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidOperation: (:) [],RuntimeException + FullyQualifiedErrorId : InvokeMethodOnNull 使用“1”个参数调用“Reverse”时发生异常:“值不能为 null。 参数名: array” 所在位置 C:\Program Files\WindowsPowerShell\Modules\Pester\5.7.1\Pester.psm1:1026 字符: 70 + ... $history = $state.Stack.ToArray(); [Array]::Reverse($history); $histo ... + ~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (:) [], MethodInvocationException + FullyQualifiedErrorId : ArgumentNullException 不能对 Null 值表达式调用方法。 所在位置 C:\Program Files\WindowsPowerShell\Modules\Pester\5.7.1\Pester.psm1:1051 字符: 5 + $state.CurrentBlock.Tests.Add($Test) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidOperation: (:) [],RuntimeException + FullyQualifiedErrorId : InvokeMethodOnNull 不能对 Null 值表达式调用方法。 所在位置 C:\Program Files\WindowsPowerShell\Modules\Pester\5.7.1\Pester.psm1:1052 字符: 5 + $state.CurrentBlock.Order.Add($Test) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidOperation: (:) [],RuntimeException + FullyQualifiedErrorId : InvokeMethodOnNull 不能对 Null 值表达式调用方法。 所在位置 C:\Program Files\WindowsPowerShell\Modules\Pester\5.7.1\Pester.psm1:1026 字符: 35 + ... ath = @(<# Get full name #> $history = $state.Stack.ToArray(); [Array ... + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidOperation: (:) [],RuntimeException + FullyQualifiedErrorId : InvokeMethodOnNull 使用“1”个参数调用“Reverse”时发生异常:“值不能为 null。 参数名: array” 所在位置 C:\Program Files\WindowsPowerShell\Modules\Pester\5.7.1\Pester.psm1:1026 字符: 70 + ... $history = $state.Stack.ToArray(); [Array]::Reverse($history); $histo ... + ~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (:) [], MethodInvocationException + FullyQualifiedErrorId : ArgumentNullException 不能对 Null 值表达式调用方法。 所在位置 C:\Program Files\WindowsPowerShell\Modules\Pester\5.7.1\Pester.psm1:1051 字符: 5 + $state.CurrentBlock.Tests.Add($Test) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidOperation: (:) [],RuntimeException + FullyQualifiedErrorId : InvokeMethodOnNull 不能对 Null 值表达式调用方法。 所在位置 C:\Program Files\WindowsPowerShell\Modules\Pester\5.7.1\Pester.psm1:1052 字符: 5 + $state.CurrentBlock.Order.Add($Test) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidOperation: (:) [],RuntimeException + FullyQualifiedErrorId : InvokeMethodOnNull 不能对 Null 值表达式调用方法。 所在位置 C:\Program Files\WindowsPowerShell\Modules\Pester\5.7.1\Pester.psm1:1026 字符: 35 + ... ath = @(<# Get full name #> $history = $state.Stack.ToArray(); [Array ... + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidOperation: (:) [],RuntimeException + FullyQualifiedErrorId : InvokeMethodOnNull 使用“1”个参数调用“Reverse”时发生异常:“值不能为 null。 参数名: array” 所在位置 C:\Program Files\WindowsPowerShell\Modules\Pester\5.7.1\Pester.psm1:1026 字符: 70 + ... $history = $state.Stack.ToArray(); [Array]::Reverse($history); $histo ... + ~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (:) [], MethodInvocationException + FullyQualifiedErrorId : ArgumentNullException 不能对 Null 值表达式调用方法。 所在位置 C:\Program Files\WindowsPowerShell\Modules\Pester\5.7.1\Pester.psm1:1051 字符: 5 + $state.CurrentBlock.Tests.Add($Test) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidOperation: (:) [],RuntimeException + FullyQualifiedErrorId : InvokeMethodOnNull 不能对 Null 值表达式调用方法。 所在位置 C:\Program Files\WindowsPowerShell\Modules\Pester\5.7.1\Pester.psm1:1052 字符: 5 + $state.CurrentBlock.Order.Add($Test) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidOperation: (:) [],RuntimeException + FullyQualifiedErrorId : InvokeMethodOnNull PS C:\Users\Administrator> It "应处理网络中断" { >> # 使用Fiddler或Charles模拟网络中断 >> } >> 不能对 Null 值表达式调用方法。 所在位置 C:\Program Files\WindowsPowerShell\Modules\Pester\5.7.1\Pester.psm1:1026 字符: 35 + ... ath = @(<# Get full name #> $history = $state.Stack.ToArray(); [Array ... + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidOperation: (:) [],RuntimeException + FullyQualifiedErrorId : InvokeMethodOnNull 使用“1”个参数调用“Reverse”时发生异常:“值不能为 null。 参数名: array” 所在位置 C:\Program Files\WindowsPowerShell\Modules\Pester\5.7.1\Pester.psm1:1026 字符: 70 + ... $history = $state.Stack.ToArray(); [Array]::Reverse($history); $histo ... + ~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (:) [], MethodInvocationException + FullyQualifiedErrorId : ArgumentNullException 不能对 Null 值表达式调用方法。 所在位置 C:\Program Files\WindowsPowerShell\Modules\Pester\5.7.1\Pester.psm1:1051 字符: 5 + $state.CurrentBlock.Tests.Add($Test) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidOperation: (:) [],RuntimeException + FullyQualifiedErrorId : InvokeMethodOnNull 不能对 Null 值表达式调用方法。 所在位置 C:\Program Files\WindowsPowerShell\Modules\Pester\5.7.1\Pester.psm1:1052 字符: 5 + $state.CurrentBlock.Order.Add($Test) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidOperation: (:) [],RuntimeException + FullyQualifiedErrorId : InvokeMethodOnNull PS C:\Users\Administrator>
08-15
function Invoke-CurlRequest { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string]$Url, [ValidateSet('Object')] [string]$OutputType = 'Object', [string]$Method = "GET", [hashtable]$Headers = @{}, [string]$Body, [string]$ContentType = "application/json" ) # 保存原始语言环境 $originalLang = $env:LANG $originalLC_ALL = $env:LC_ALL try { # 设置英文环境确保错误消息为英文 $env:LANG = 'C' $env:LC_ALL = 'C' # 构建 curl 命令参数 $curlArgs = @( $Url, "-X", $Method, "-s", # 静默模式 "-i", # 包含响应头 "-L", # 跟随重定向 "--show-error" ) # 添加请求头 if (-not $Headers.ContainsKey("Content-Type") -and $Body) { $Headers["Content-Type"] = $ContentType } foreach ($key in $Headers.Keys) { $curlArgs += "-H" $curlArgs += "$key`: $($Headers[$key])" } # 添加请求体 if ($Body) { $curlArgs += "-d" $curlArgs += $Body } # 执行 curl 命令并捕获输出 $result = curl.exe @curlArgs 2>&1 $exitCode = $LASTEXITCODE # 处理 curl 错误 if ($exitCode -ne 0) { $errorMsg = ($result | Where-Object { $_ -match 'curl: \(\d+\)' }) -join "`n" return [PSCustomObject]@{ Status = 'Error' ErrorMessage = "curl command failed (exit code: $exitCode): $errorMsg" Content = $errorMsg Url = $Url Method = $Method } } # 分离响应头和内容 $headerEndIndex = [array]::IndexOf($result, "") if ($headerEndIndex -eq -1) { $headerEndIndex = $result.Count - 1 } $headers = $result[0..($headerEndIndex)] $content = $result[($headerEndIndex + 1)..($result.Count - 1)] -join "`n" # 提取状态码 $statusLine = $headers[0] $statusCode = [regex]::Match($statusLine, 'HTTP/\d\.\d (\d+)').Groups[1].Value # 特殊处理 204 No Content if ($statusCode -eq '204') { return [PSCustomObject]@{ Status = 'NoContent' Content = "" Url = $Url Method = $Method } } # 尝试解析 JSON 响应(仅当 OutputType = Object) $responseObj = $null $responseStatus = [System.Net.HttpStatusCode]$statusCode if ($OutputType -eq 'Object' -and -not [string]::IsNullOrEmpty($content)) { try { $responseObj = $content | ConvertFrom-Json -ErrorAction Stop # 智能提取嵌套数据 if ($responseObj.PSObject.Properties.Name -contains "json") { $responseObj = $responseObj.json } elseif ($responseObj.PSObject.Properties.Name -contains "data") { $responseObj = $responseObj.data } } catch { # 非 JSON 响应保持原始内容 } } # 构建响应对象 return [PSCustomObject]@{ Status = $responseStatus.ToString() Content = $content Data = $responseObj Url = $Url Method = $Method } } catch { return [PSCustomObject]@{ Status = 'Error' ErrorMessage = $_.Exception.Message Content = $_.Exception.Message Url = $Url Method = $Method } } finally { # 恢复原始语言环境 $env:LANG = $originalLang $env:LC_ALL = $originalLC_ALL } } “function Invoke-CurlRequest { <# .SYNOPSIS 执行 curl 请求并返回标准化响应对象 .DESCRIPTION 始终返回包含以下属性的对象: - Status: 响应状态 (Success, NoContent, NonJsonResponse, Error) - Content: 原始响应内容 (字符串) - Data: 解析后的数据对象 (仅当OutputType=Object且解析成功时存在) - ErrorMessage: 错误消息 (仅当Status=Error时存在) - Url: 请求的URL - Method: 使用的HTTP方法 - StatusCode: HTTP状态码 (如果可获取) #> param( [Parameter(Mandatory=$true)] [string]$Url, [string]$Method = "GET", [hashtable]$Headers = @{}, [string]$Body, [string]$ContentType = "application/json", [ValidateSet("Raw", "Object")] [string]$OutputType = "Raw", [switch]$IncludeHeaders ) # 保存原始语言环境 $originalLang = $env:LANG $originalLC_ALL = $env:LC_ALL # 初始化响应对象模板 $responseTemplate = [PSCustomObject]@{ Status = $null Content = $null Data = $null ErrorMessage = $null Url = $Url Method = $Method StatusCode = $null } try { # 设置英文环境防止乱码 $env:LANG = 'C' $env:LC_ALL = 'C' # 构建 curl 命令参数 $curlArgs = @( $Url, "-X", $Method, "--silent", "--show-error" ) # 添加 -i 参数获取响应头 if ($IncludeHeaders) { $curlArgs += "-i" } # 添加内容类型头 if (-not $Headers.ContainsKey("Content-Type")) { $Headers["Content-Type"] = $ContentType } # 添加请求头 foreach ($key in $Headers.Keys) { $curlArgs += "-H" $curlArgs += "$key`: $($Headers[$key])" } # 添加请求体 if ($Body) { $curlArgs += "-d" $curlArgs += $Body } # 执行 curl 命令并捕获输出和错误 $rawOutput = curl.exe @curlArgs 2>&1 $curlExitCode = $LASTEXITCODE # 处理原始输出为字符串 $contentString = if ($null -ne $rawOutput) { if ($rawOutput -is [array]) { $rawOutput -join "`n" } else { $rawOutput.ToString() } } else { $null } # 尝试提取HTTP状态码 $statusCode = $null if ($IncludeHeaders -and $contentString -match "(?s)^HTTP/\d+\.\d+ (\d{3})") { $statusCode = [int]$matches[1] } # 检查 curl 命令是否成功 if ($curlExitCode -ne 0) { # 创建错误响应 $errorResponse = $responseTemplate.PSObject.Copy() $errorResponse.Status = "Error" $errorResponse.ErrorMessage = $contentString -replace '.*curl: \(\d+\) (.*)', '$1' $errorResponse.Content = $contentString $errorResponse.StatusCode = $statusCode return $errorResponse } # 处理空响应 if ([string]::IsNullOrWhiteSpace($contentString)) { $emptyResponse = $responseTemplate.PSObject.Copy() $emptyResponse.Status = "NoContent" $emptyResponse.StatusCode = $statusCode return $emptyResponse } # 处理原始输出类型 if ($OutputType -eq "Raw") { $rawResponse = $responseTemplate.PSObject.Copy() $rawResponse.Status = "Success" $rawResponse.Content = $contentString $rawResponse.StatusCode = $statusCode return $rawResponse } # 尝试解析 JSON 响应 try { # 如果包含响应头,需要分离头部和主体 $jsonContent = if ($IncludeHeaders) { if ($contentString -match "(?s)\r?\n\r?\n(.*)") { $matches[1] } else { $contentString } } else { $contentString } $responseObj = $jsonContent | ConvertFrom-Json -ErrorAction Stop # 创建成功响应 $successResponse = $responseTemplate.PSObject.Copy() $successResponse.Status = "Success" $successResponse.Content = $contentString $successResponse.Data = $responseObj $successResponse.StatusCode = $statusCode # 智能提取嵌套数据 if ($responseObj.PSObject.Properties.Name -contains "json") { $successResponse.Data = $responseObj.json } elseif ($responseObj.PSObject.Properties.Name -contains "data") { try { $successResponse.Data = $responseObj.data | ConvertFrom-Json } catch { $successResponse.Data = $responseObj.data } } return $successResponse } catch { # 创建非JSON响应 $nonJsonResponse = $responseTemplate.PSObject.Copy() $nonJsonResponse.Status = "NonJsonResponse" $nonJsonResponse.Content = $contentString $nonJsonResponse.StatusCode = $statusCode return $nonJsonResponse } } catch { # 创建异常响应 $exceptionResponse = $responseTemplate.PSObject.Copy() $exceptionResponse.Status = "Error" $exceptionResponse.ErrorMessage = $_.Exception.Message -replace '.*curl: \(\d+\) (.*)', '$1' $exceptionResponse.StatusCode = $statusCode return $exceptionResponse } finally { # 恢复原始语言环境 $env:LANG = $originalLang $env:LC_ALL = $originalLC_ALL } } ”
08-17
import sys import numpy as np import pyaudio from scipy.signal import get_window from scipy.interpolate import interp1d from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QComboBox, QLabel, QPushButton, QLineEdit, QMessageBox, QGroupBox, QRadioButton) from PyQt5.QtCore import Qt, QTimer from PyQt5.QtGui import QIntValidator, QDoubleValidator from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas from matplotlib.figure import Figure # 从您提供的数据中提取的精确计权曲线 FREQUENCIES = [1, 1.25, 1.6, 2, 2.5, 3.15, 4, 5, 6.3, 8, 10, 12.5, 16, 20, 25, 31.5, 40, 50, 63, 80, 100, 125, 160, 200, 250, 315, 400, 500, 630, 800, 1000, 1250, 1600, 2000, 2500, 3150, 4000, 5000, 6300, 8000, 10000, 12500, 16000, 20000, 25000, 31500, 40000, 50000, 63000, 80000, 100000] A_WEIGHTING = [-148.58, -140.83, -132.28, -124.55, -116.85, -108.89, -100.72, -93.14, -85.4, -77.55, -70.43, -63.58, -56.42, -50.39, -44.82, -39.53, -34.54, -30.27, -26.22, -22.4, -19.14, -16.19, -13.24, -10.85, -8.67, -6.64, -4.77, -3.25, -1.91, -0.79, 0, 0.58, 0.99, 1.2, 1.27, 1.2, 0.96, 0.55, -0.12, -1.15, -2.49, -4.25, -6.71, -9.35, -12.33, -15.7, -19.41, -23.02, -26.85, -30.88, -34.68] B_WEIGHTING = [-96.41, -90.6, -84.19, -78.41, -72.64, -66.69, -60.59, -54.95, -49.21, -43.43, -38.24, -33.32, -28.28, -24.16, -20.48, -17.13, -14.1, -11.63, -9.36, -7.31, -5.65, -4.23, -2.94, -2.04, -1.36, -0.85, -0.5, -0.28, -0.13, -0.04, 0, 0.01, -0.02, -0.09, -0.21, -0.4, -0.73, -1.18, -1.89, -2.94, -4.3, -6.07, -8.53, -11.17, -14.16, -17.53, -21.24, -24.85, -28.68, -32.71, -36.51] C_WEIGHTING = [-52.51, -48.65, -44.38, -40.53, -36.7, -32.76, -28.73, -25.03, -21.3, -17.59, -14.33, -11.34, -8.43, -6.22, -4.44, -3.03, -1.98, -1.3, -0.82, -0.5, -0.3, -0.17, -0.08, -0.03, 0, 0.02, 0.03, 0.03, 0.03, 0.02, 0, -0.03, -0.09, -0.17, -0.3, -0.5, -0.83, -1.29, -1.99, -3.05, -4.41, -6.18, -8.63, -11.28, -14.26, -17.64, -21.35, -24.95, -28.79, -32.82, -36.62] # 组合数据 STANDARD_WEIGHTING = { "A": list(zip(FREQUENCIES, A_WEIGHTING)), "B": list(zip(FREQUENCIES, B_WEIGHTING)), "C": list(zip(FREQUENCIES, C_WEIGHTING)) } class RealTimeFFT(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("实时声级计(精确计权曲线)") self.setGeometry(100, 100, 1400, 900) self.setMinimumWidth(800) # 设置合理的最小宽度,避免界面元素被压缩过度 self.setMinimumHeight(600) # 设置合理的最小高度 self.audio = pyaudio.PyAudio() self.stream = None self.is_running = False self.weighting_type = "A" # 默认 A 计权 # 创建更灵活的画布 self.fig = Figure(figsize=(10, 7), dpi=100, tight_layout=True) self.ax_spectrum = self.fig.add_subplot(2, 1, 1) # 主频谱 self.ax_standard = self.fig.add_subplot(2, 1, 2) # 标准曲线对比 self.canvas = FigureCanvas(self.fig) # 控件:选择麦克风 self.mic_label = QLabel("选择麦克风:") self.mic_combo = QComboBox() self.populate_mic_devices() # 控件:选择窗函数 self.window_label = QLabel("选择窗函数:") self.window_combo = QComboBox() self.window_combo.addItems(["blackman", "hamming", "hann", "bartlett", "boxcar"]) # 控件:增益补偿 self.gain_label = QLabel("增益补偿 (dB):") self.gain_combo = QComboBox() self.gain_combo.addItems(["150", "160", "170", "180", "190", "200"]) self.gain_combo.setCurrentText("150") # 控件:刷新速度(输入框) self.refresh_label = QLabel("刷新速度 (ms):") self.refresh_edit = QLineEdit("50") self.refresh_edit.setValidator(QIntValidator(10, 1000)) # 新增坐标轴范围输入控件 self.x_start_label = QLabel("X轴起始频点:") self.x_start_edit = QLineEdit("10") self.x_start_edit.setValidator(QDoubleValidator(0.1, 100000, 2)) self.x_end_label = QLabel("X轴截止频点:") self.x_end_edit = QLineEdit("20000") self.x_end_edit.setValidator(QDoubleValidator(0.1, 100000, 2)) self.y_start_label = QLabel("Y轴起始值:") self.y_start_edit = QLineEdit("-50") self.y_start_edit.setValidator(QDoubleValidator(-100, 100, 2)) self.y_end_label = QLabel("Y轴截止值:") self.y_end_edit = QLineEdit("100") self.y_end_edit.setValidator(QDoubleValidator(-100, 100, 2)) # 控件:计权选择(A、B、C) self.weighting_group = QGroupBox("频率计权(精确标准)") self.weighting_layout = QHBoxLayout() self.a_weighting_radio = QRadioButton("A计权") self.b_weighting_radio = QRadioButton("B计权") self.c_weighting_radio = QRadioButton("C计权") self.a_weighting_radio.setChecked(True) self.weighting_layout.addWidget(self.a_weighting_radio) self.weighting_layout.addWidget(self.b_weighting_radio) self.weighting_layout.addWidget(self.c_weighting_radio) self.weighting_group.setLayout(self.weighting_layout) self.a_weighting_radio.clicked.connect(lambda: self.set_weighting("A")) self.b_weighting_radio.clicked.connect(lambda: self.set_weighting("B")) self.c_weighting_radio.clicked.connect(lambda: self.set_weighting("C")) # 控件:开始/停止按钮 self.start_btn = QPushButton("开始") self.start_btn.clicked.connect(self.toggle_stream) self.stop_btn = QPushButton("停止") self.stop_btn.clicked.connect(self.stop_stream) self.stop_btn.setEnabled(False) # 统计信息 self.stats_label = QLabel("当前声级: -- dB 峰值: -- dB") # 布局设置:新增坐标轴输入框布局 input_layout = QHBoxLayout() input_layout.addWidget(self.x_start_label) input_layout.addWidget(self.x_start_edit) input_layout.addWidget(self.x_end_label) input_layout.addWidget(self.x_end_edit) input_layout.addWidget(self.y_start_label) input_layout.addWidget(self.y_start_edit) input_layout.addWidget(self.y_end_label) input_layout.addWidget(self.y_end_edit) # 优化控制布局,添加伸缩项使控件分布更均匀 control_layout = QHBoxLayout() control_layout.addWidget(self.mic_label) control_layout.addWidget(self.mic_combo) control_layout.addWidget(self.window_label) control_layout.addWidget(self.window_combo) control_layout.addWidget(self.gain_label) control_layout.addWidget(self.gain_combo) control_layout.addWidget(self.refresh_label) control_layout.addWidget(self.refresh_edit) control_layout.addLayout(input_layout) control_layout.addWidget(self.weighting_group) control_layout.addWidget(self.start_btn) control_layout.addWidget(self.stop_btn) control_layout.addStretch() # 添加伸缩项 stats_layout = QHBoxLayout() stats_layout.addWidget(self.stats_label) # 主布局设置 main_layout = QVBoxLayout() main_layout.addLayout(control_layout) main_layout.addLayout(stats_layout) main_layout.addWidget(self.canvas) # 设置布局伸缩性和边距 main_layout.setContentsMargins(5, 5, 5, 5) # 设置适当边距 main_layout.setSpacing(5) main_layout.setStretch(2, 1) # 让画布区域可伸缩 central_widget = QWidget() central_widget.setLayout(main_layout) self.setCentralWidget(central_widget) # FFT 参数初始化 self.CHUNK = 1024 * 2 self.FORMAT = pyaudio.paInt16 self.CHANNELS = 1 self.RATE = 44100 self.freq_axis = np.fft.rfftfreq(self.CHUNK, 1.0 / self.RATE) # 初始化定时器 self.timer = QTimer(self) self.timer.timeout.connect(self.update_plot) # 预绘制标准曲线 self.plot_standard_weighting() def populate_mic_devices(self): """获取麦克风设备列表""" info = self.audio.get_host_api_info_by_index(0) num_devices = info.get('deviceCount') for i in range(num_devices): device = self.audio.get_device_info_by_host_api_device_index(0, i) if device['maxInputChannels'] > 0: self.mic_combo.addItem(device['name'], i) def set_weighting(self, weighting): """设置计权类型并更新对比曲线""" self.weighting_type = weighting self.plot_standard_weighting() def start_stream(self): """启动音频流""" device_index = self.mic_combo.currentData() try: refresh_ms = int(self.refresh_edit.text()) if not (10 <= refresh_ms <= 1000): raise ValueError("刷新速度需在10-1000ms之间") self.stream = self.audio.open( input_device_index=device_index, format=self.FORMAT, channels=self.CHANNELS, rate=self.RATE, input=True, frames_per_buffer=self.CHUNK ) self.is_running = True self.timer.start(refresh_ms) self.start_btn.setEnabled(False) self.stop_btn.setEnabled(True) print(f"已启动音频流,设备: {self.mic_combo.currentText()}") except Exception as e: QMessageBox.critical(self, "错误", f"无法启动音频流: {str(e)}") def stop_stream(self): """停止音频流""" self.is_running = False self.timer.stop() if self.stream: self.stream.stop_stream() self.stream.close() self.stream = None self.start_btn.setEnabled(True) self.stop_btn.setEnabled(False) self.stats_label.setText("当前声级: -- dB 峰值: -- dB") def toggle_stream(self): if not self.is_running: self.start_stream() def interpolate_weighting(self, freq): """根据标准数据插值计算计权修正值""" std_data = STANDARD_WEIGHTING[self.weighting_type] freqs = np.array([f for f, _ in std_data]) weights = np.array([w for _, w in std_data]) return np.interp(freq, freqs, weights) def plot_standard_weighting(self): """绘制标准计权曲线""" self.ax_standard.clear() std_data = STANDARD_WEIGHTING[self.weighting_type] freqs = [f for f, _ in std_data] weights = [w for _, w in std_data] self.ax_standard.plot(freqs, weights, color='red', label=f"标准 {self.weighting_type}计权") self.ax_standard.axvline(x=1000, color='gray', linestyle='--', label="1kHz 参考线") self.ax_standard.axhline(y=0, color='gray', linestyle='--') self.ax_standard.set_xscale('log') self.ax_standard.set_xlabel("频率 (Hz)") self.ax_standard.set_ylabel("计权修正值 (dB)") self.ax_standard.set_xlim(10, 20000) self.ax_standard.grid(True, linestyle='--', alpha=0.7) self.ax_standard.legend() self.fig.tight_layout() # 确保布局紧凑 self.canvas.draw() def update_plot(self): """更新频谱和统计信息,根据输入框值更新坐标轴范围""" if not self.is_running or not self.stream: return try: # 读取音频数据 data = self.stream.read(self.CHUNK) data_np = np.frombuffer(data, dtype=np.int16) / 32768.0 # 应用窗函数 window_name = self.window_combo.currentText() window = get_window(window_name, self.CHUNK) windowed_data = data_np * window # 计算FFT fft_data = np.fft.rfft(windowed_data) magnitude = np.abs(fft_data) * 2 / np.sum(window) magnitude[0] /= 2 # 直流分量修正 # 增益补偿 gain = float(self.gain_combo.currentText()) magnitude *= 10 ** (gain / 20) # 计算计权修正 weighting = np.array([self.interpolate_weighting(f) for f in self.freq_axis]) db_data = 20 * np.log10(magnitude + 1e-10) + weighting # 获取坐标轴范围输入值 x_start = float(self.x_start_edit.text()) x_end = float(self.x_end_edit.text()) y_start = float(self.y_start_edit.text()) y_end = float(self.y_end_edit.text()) # 统计信息(在设置的X轴范围内) valid_indices = (self.freq_axis >= x_start) & (self.freq_axis <= x_end) valid_db = db_data[valid_indices] avg_db = np.mean(valid_db) if valid_db.size > 0 else 0 max_db = np.max(valid_db) if valid_db.size > 0 else 0 self.stats_label.setText(f"当前声级: {avg_db:.1f} dB 峰值: {max_db:.1f} dB") # 绘制频谱 self.ax_spectrum.clear() self.ax_spectrum.plot(self.freq_axis, db_data, color='blue', label="实时频谱") self.ax_spectrum.set_title(f"实时FFT频谱({self.weighting_type}计权)") self.ax_spectrum.set_xlabel("频率 (Hz)") self.ax_spectrum.set_ylabel("声压级 (dB)") self.ax_spectrum.set_xscale('log') self.ax_spectrum.set_xlim(x_start, x_end) self.ax_spectrum.set_ylim(y_start, y_end) self.ax_spectrum.grid(True, linestyle='--', alpha=0.7) self.ax_spectrum.legend() # 刷新画布 self.fig.tight_layout() # 确保布局紧凑 self.canvas.draw() except Exception as e: print(f"更新失败: {e}") self.stop_stream() def closeEvent(self, event): """关闭时释放资源""" self.stop_stream() self.audio.terminate() event.accept() if __name__ == '__main__': app = QApplication(sys.argv) font = app.font() font.setFamily("SimHei") # 设置中文字体支持 app.setFont(font) window = RealTimeFFT() window.show() sys.exit(app.exec_())这个代码在pycharm上正常运行,希望同步生成手机apk安装包给手机安装成app后使用,教我一步步打包,我是超级新手,所有的步骤都要很详细,比如输入命令时要告诉我是在powershell,还是pycharm的终端输入类似这样的动作要很详细
最新发布
09-03
import os import cv2 import subprocess import shutil import tempfile from PIL import Image from tqdm import tqdm import numpy as np import queue import threading import onnxruntime as ort import platform import re import sys # 设置ONNX Runtime日志级别为警告(减少输出) os.environ['ORT_LOG_LEVEL'] = '3' os.environ["CUDA_VISIBLE_DEVICES"] = "0" def get_cuda_version(): """获取安装的CUDA版本""" try: # 检查环境变量 cuda_path = os.environ.get('CUDA_PATH') if cuda_path: version_file = os.path.join(cuda_path, "version.txt") if os.path.exists(version_file): with open(version_file, 'r') as f: return f.read().strip() # 检查系统路径中的dll if platform.system() == "Windows": # 检查常见的CUDA安装路径 possible_paths = [ os.environ.get('CUDA_PATH', ''), r'C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA', r'C:\CUDA' ] for base_path in possible_paths: if os.path.exists(base_path): for version_dir in os.listdir(base_path): if re.match(r'v\d+\.\d+', version_dir): version_file = os.path.join(base_path, version_dir, "version.txt") if os.path.exists(version_file): with open(version_file, 'r') as f: return f.read().strip() # 检查PATH中的dll try: result = subprocess.run(['where', 'cudart64_*.dll'], capture_output=True, text=True) if result.returncode == 0: dll_path = result.stdout.splitlines()[0].strip() version_match = re.search(r'cudart64_(\d+)\.dll', dll_path) if version_match: version_num = version_match.group(1) return f"CUDA {version_num[0]}.{version_num[1:]}" except: pass return "无法确定CUDA版本(请检查CUDA_PATH环境变量)" except Exception as e: return f"获取CUDA版本时出错: {str(e)}" def get_cudnn_version(): """获取安装的cuDNN版本""" try: cuda_path = os.environ.get('CUDA_PATH') if not cuda_path: return "未找到CUDA_PATH环境变量" # 检查头文件 header_path = os.path.join(cuda_path, "include", "cudnn_version.h") if not os.path.exists(header_path): header_path = os.path.join(cuda_path, "include", "cudnn.h") if os.path.exists(header_path): with open(header_path, 'r') as f: content = f.read() major = re.search(r"#define CUDNN_MAJOR\s+(\d+)", content) minor = re.search(r"#define CUDNN_MINOR\s+(\d+)", content) patch = re.search(r"#define CUDNN_PATCHLEVEL\s+(\d+)", content) if major and minor and patch: return f"cuDNN {major.group(1)}.{minor.group(1)}.{patch.group(1)}" # 检查库文件 lib_dir = os.path.join(cuda_path, "lib", "x64") if os.path.exists(lib_dir): for file in os.listdir(lib_dir): if file.startswith("cudnn64_") and file.endswith(".dll"): version = re.search(r"cudnn64_(\d+)\.dll", file) if version: version_num = version.group(1) return f"cuDNN {version_num[0]}.{version_num[1:]}" return "无法确定cuDNN版本(请检查cudnn64_*.dll文件)" except Exception as e: return f"获取cuDNN版本时出错: {str(e)}" def check_onnxruntime_gpu(): """检查ONNX Runtime是否支持GPU""" try: # 检查可用提供程序 available_providers = ort.get_available_providers() gpu_supported = 'CUDAExecutionProvider' in available_providers # 如果支持GPU但初始化失败,尝试创建简单会话 if gpu_supported: try: # 创建简单的ONNX模型 import onnx from onnx import helper, TensorProto # 创建一个简单的模型 input_name = 'input' output_name = 'output' shape = [1, 3, 224, 224] node_def = helper.make_node( 'Identity', inputs=[input_name], outputs=[output_name] ) graph_def = helper.make_graph( [node_def], 'test-model', [helper.make_tensor_value_info(input_name, TensorProto.FLOAT, shape)], [helper.make_tensor_value_info(output_name, TensorProto.FLOAT, shape)] ) model_def = helper.make_model(graph_def, producer_name='onnx-example') # 创建临时模型文件 with tempfile.NamedTemporaryFile(suffix='.onnx', delete=False) as temp_file: onnx.save(model_def, temp_file.name) session = ort.InferenceSession(temp_file.name, providers=['CUDAExecutionProvider']) return f"支持GPU: {gpu_supported}, 当前会话使用: {session.get_providers()}" except Exception as e: return f"支持GPU: {gpu_supported}, 但初始化失败: {str(e)}" return f"支持GPU: {gpu_supported}, 可用提供程序: {available_providers}" except Exception as e: return f"检查ONNX Runtime GPU支持时出错: {str(e)}" def has_ffmpeg(): """检查系统是否安装了ffmpeg""" try: subprocess.run(['ffmpeg', '-version'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) return True except (FileNotFoundError, subprocess.CalledProcessError): # 检查常见安装路径 if platform.system() == "Windows": possible_paths = [ r'C:\Program Files\ffmpeg\bin\ffmpeg.exe', r'C:\ffmpeg\bin\ffmpeg.exe', r'C:\Program Files (x86)\ffmpeg\bin\ffmpeg.exe' ] for path in possible_paths: if os.path.exists(path): return True return False def add_audio_to_video(video_path, original_video_path, output_path, fps): """ 使用ffmpeg为生成的视频添加原始音频 参数: video_path: 无声视频路径 original_video_path: 原始视频路径(包含音频) output_path: 最终输出路径 fps: 视频帧率 """ if not has_ffmpeg(): print("警告: ffmpeg未安装,无法添加音频。请安装ffmpeg以支持音频处理。") shutil.copyfile(video_path, output_path) return output_path # 使用临时文件避免覆盖问题 with tempfile.NamedTemporaryFile(suffix='.mp4', delete=False) as temp_file: temp_path = temp_file.name try: # 检查原始视频是否有音频流 check_audio_cmd = [ 'ffprobe', '-v', 'error', '-select_streams', 'a', '-show_entries', 'stream=codec_type', '-of', 'default=noprint_wrappers=1:nokey=1', original_video_path ] result = subprocess.run(check_audio_cmd, capture_output=True, text=True) has_audio = 'audio' in result.stdout if not has_audio: print(f"警告: 原始视频 '{os.path.basename(original_video_path)}' 没有音频轨道") shutil.copyfile(video_path, output_path) return output_path # 尝试使用软件编码 cmd = [ 'ffmpeg', '-y', # 覆盖输出文件 '-r', str(fps), # 添加帧率 '-i', video_path, # 无声视频 '-i', original_video_path, # 原始视频(包含音频) '-c:v', 'libx264', # 使用H.264编码 '-preset', 'fast', # 编码速度预设 '-crf', '23', # 质量控制 '-pix_fmt', 'yuv420p', # 视频要求的像素格式 '-c:a', 'aac', # 音频编码 '-b:a', '128k', # 音频比特率 '-map', '0:v:0', # 选择第一个视频流 '-map', '1:a:0', # 选择第二个文件的音频流 '-shortest', # 以最短流结束 '-movflags', '+faststart', # 流媒体优化 temp_path ] # 执行命令 try: subprocess.run(cmd, check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) except subprocess.CalledProcessError as e: print(f"音频合并失败: {e}") print("将使用无声视频") shutil.copyfile(video_path, output_path) return output_path # 移动临时文件到最终位置 shutil.move(temp_path, output_path) return output_path except Exception as e: print(f"音频合并过程中出错: {str(e)}") print("将使用无声视频") shutil.copyfile(video_path, output_path) return output_path finally: # 清理临时文件 if os.path.exists(temp_path): os.remove(temp_path) class Videocap: def __init__(self, video, model_name, limit=1280): self.model_name = model_name vid = cv2.VideoCapture(video) if not vid.isOpened(): raise ValueError(f"无法打开视频文件: {video}") width = int(vid.get(cv2.CAP_PROP_FRAME_WIDTH)) height = int(vid.get(cv2.CAP_PROP_FRAME_HEIGHT)) self.total = int(vid.get(cv2.CAP_PROP_FRAME_COUNT)) self.fps = vid.get(cv2.CAP_PROP_FPS) self.ori_width, self.ori_height = width, height max_edge = max(width, height) scale_factor = limit / max_edge if max_edge > limit else 1. height = int(round(height * scale_factor)) width = int(round(width * scale_factor)) self.width, self.height = self.to_16s(width), self.to_16s(height) # 修改为16的倍数 self.count = 0 self.cap = vid self.ret, frame = self.cap.read() self.cap.set(cv2.CAP_PROP_POS_FRAMES, 0) self.q = queue.Queue(maxsize=100) t = threading.Thread(target=self._reader) t.daemon = True t.start() def _reader(self): while True: self.ret, frame = self.cap.read() if not self.ret: break frame = np.asarray(self.process_frame(frame, self.width, self.height)) self.q.put(frame) self.count += 1 self.cap.release() def read(self): if self.q.empty() and not self.ret: return None f = self.q.get() self.q.task_done() return f def to_16s(self, x): """确保分辨率是16的倍数(H.264兼容性要求)""" if x < 256: return 256 # 确保宽度和高度都能被16整除 return x - x % 16 def process_frame(self, img, width, height): img = Image.fromarray(img[:, :, ::-1]).resize((width, height), Image.Resampling.BILINEAR) img = np.array(img).astype(np.float32) / 127.5 - 1.0 return np.expand_dims(img, axis=0) class Cartoonizer(): def __init__(self, model_path, device="gpu"): self.model_path = model_path self.device = device self.name = os.path.basename(model_path).rsplit('.', 1)[0] # 打印环境信息 print("\n" + "="*50) print("环境检查:") print(f"CUDA 版本: {get_cuda_version()}") print(f"cuDNN 版本: {get_cudnn_version()}") print(f"ONNX Runtime GPU 支持: {check_onnxruntime_gpu()}") print("="*50 + "\n") # 配置提供程序 - 强制使用CPU providers = ['CPUExecutionProvider'] print("强制使用CPUExecutionProvider") # 创建ONNX会话 session_options = ort.SessionOptions() session_options.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_ALL try: self.sess_land = ort.InferenceSession(model_path, sess_options=session_options, providers=providers) print(f"ONNX Runtime 使用的提供程序: {self.sess_land.get_providers()}") except Exception as e: print(f"创建ONNX会话失败: {str(e)}") print("尝试使用默认提供程序...") self.sess_land = ort.InferenceSession(model_path, sess_options=session_options) # 打印输入输出信息 print("模型输入信息:") for input in self.sess_land.get_inputs(): print(f" Name: {input.name}, Shape: {input.shape}, Type: {input.type}") print("模型输出信息:") for output in self.sess_land.get_outputs(): print(f" Name: {output.name}, Shape: {output.shape}, Type: {output.type}") def post_precess(self, img, wh): img = (img.squeeze() + 1.) / 2 * 255 img = img.clip(0, 255).astype(np.uint8) img = Image.fromarray(img).resize((wh[0], wh[1]), Image.Resampling.BILINEAR) img = np.array(img).astype(np.uint8) return img def process_video(self, video_path, output_path): """处理视频并添加原始音频""" # 创建临时无声视频文件 temp_dir = tempfile.mkdtemp() temp_video_path = os.path.join(temp_dir, "temp_no_audio.mp4") try: # 处理视频(无声) vid = Videocap(video_path, self.name) # 使用FFmpeg直接编码视频(更高效) if has_ffmpeg(): print("使用FFmpeg进行视频编码") return self.process_video_with_ffmpeg(vid, video_path, output_path, temp_video_path) # 回退到OpenCV编码 print("使用OpenCV进行视频编码") return self.process_video_with_opencv(vid, video_path, output_path, temp_video_path) except Exception as e: print(f"视频处理失败: {str(e)}") # 尝试直接复制原始视频作为回退 shutil.copyfile(video_path, output_path) print(f"已回退到原始视频: {output_path}") return output_path finally: # 清理临时文件 shutil.rmtree(temp_dir, ignore_errors=True) def process_video_with_ffmpeg(self, vid, original_video_path, output_path, temp_video_path): """使用FFmpeg处理视频""" try: # 创建FFmpeg管道 ffmpeg_cmd = [ 'ffmpeg', '-y', '-f', 'rawvideo', '-vcodec', 'rawvideo', '-s', f'{vid.width}x{vid.height}', '-pix_fmt', 'rgb24', '-r', str(vid.fps), '-i', '-', '-an', # 无音频 '-vcodec', 'libx264', '-preset', 'fast', '-crf', '23', '-pix_fmt', 'yuv420p', temp_video_path ] ffmpeg_process = subprocess.Popen( ffmpeg_cmd, stdin=subprocess.PIPE, stdout=subprocess.DEVNULL, stderr=subprocess.PIPE ) num = vid.total pbar = tqdm(total=vid.total) pbar.set_description(f"处理视频: {os.path.basename(original_video_path)}") try: while num > 0: frame = vid.read() if frame is None: print("警告: 读取到空帧,提前结束") break fake_img = self.sess_land.run(None, {self.sess_land.get_inputs()[0].name: frame})[0] result_img = self.post_precess(fake_img, (vid.width, vid.height)) # 转换为RGB格式并写入管道 rgb_img = cv2.cvtColor(result_img, cv2.COLOR_BGR2RGB) ffmpeg_process.stdin.write(rgb_img.tobytes()) pbar.update(1) num -= 1 except Exception as e: print(f"处理过程中出错: {str(e)}") finally: pbar.close() ffmpeg_process.stdin.close() _, stderr = ffmpeg_process.communicate() if ffmpeg_process.returncode != 0: print(f"FFmpeg编码错误: {stderr.decode('utf-8')}") raise RuntimeError("FFmpeg编码失败") # 添加原始音频 return add_audio_to_video(temp_video_path, original_video_path, output_path, vid.fps) except Exception as e: print(f"FFmpeg处理失败: {str(e)}") print("回退到OpenCV编码") return self.process_video_with_opencv(vid, original_video_path, output_path, temp_video_path) def process_video_with_opencv(self, vid, original_video_path, output_path, temp_video_path): """使用OpenCV处理视频(回退方法)""" # 尝试不同的编码器 codec_options = [ ('mp4v', 'MPEG-4'), # 最可靠的备选 ('avc1', 'H.264/AVC'), ('h264', 'H.264/AVC'), ('x264', 'H.264/AVC'), ('vp09', 'VP9'), ('vp80', 'VP8') ] video_out = None selected_codec = None for codec, codec_name in codec_options: try: fourcc = cv2.VideoWriter_fourcc(*codec) video_out = cv2.VideoWriter( temp_video_path, fourcc, vid.fps, (vid.ori_width, vid.ori_height) ) if video_out.isOpened(): selected_codec = (codec, codec_name) print(f"使用编码器: {codec_name} ({codec})") break else: video_out.release() except Exception as e: print(f"编码器 {codec} 初始化失败: {str(e)}") video_out = None # 如果所有编码器都失败,使用默认编码器 if video_out is None or not video_out.isOpened(): print("警告: 所有编码器初始化失败,使用默认MPEG-4编码器") fourcc = cv2.VideoWriter_fourcc(*'mp4v') video_out = cv2.VideoWriter( temp_video_path, fourcc, vid.fps, (vid.ori_width, vid.ori_height) ) selected_codec = ('mp4v', 'MPEG-4 (默认)') num = vid.total pbar = tqdm(total=vid.total) pbar.set_description(f"处理视频: {os.path.basename(original_video_path)}") try: while num > 0: frame = vid.read() if frame is None: print("警告: 读取到空帧,提前结束") break fake_img = self.sess_land.run(None, {self.sess_land.get_inputs()[0].name: frame})[0] fake_img = self.post_precess(fake_img, (vid.ori_width, vid.ori_height)) video_out.write(fake_img[:, :, ::-1]) pbar.update(1) num -= 1 except Exception as e: print(f"处理过程中出错: {str(e)}") finally: pbar.close() video_out.release() # 添加原始音频 return add_audio_to_video(temp_video_path, original_video_path, output_path, vid.fps) def process_image(self, image_path, output_path): try: img = cv2.imread(image_path) if img is None: print(f"错误: 无法读取图片: {image_path}") return None ori_height, ori_width = img.shape[:2] # 计算目标尺寸 max_edge = max(ori_width, ori_height) scale_factor = 1280 / max_edge if max_edge > 1280 else 1. height = int(round(ori_height * scale_factor)) width = int(round(ori_width * scale_factor)) # 确保分辨率是16的倍数 width = width - width % 16 height = height - height % 16 # 预处理图片 img_rgb = Image.fromarray(img[:, :, ::-1]).resize((width, height), Image.Resampling.BILINEAR) img_np = np.array(img_rgb).astype(np.float32) / 127.5 - 1.0 input_data = np.expand_dims(img_np, axis=0) # 运行模型 fake_img = self.sess_land.run(None, {self.sess_land.get_inputs()[0].name: input_data})[0] # 后处理 result_img = self.post_precess(fake_img, (ori_width, ori_height)) # 保存结果 cv2.imwrite(output_path, result_img[:, :, ::-1]) return output_path except Exception as e: print(f"图片处理失败: {str(e)}") # 尝试直接复制原始图片作为回退 shutil.copyfile(image_path, output_path) print(f"已回退到原始图片: {output_path}") return output_path def videopic_to_new(params): """ 将输入目录中的图片和视频转换为动漫风格 参数: params (dict): 包含以下键的字典: "video_dir": 输入目录路径 "model": ONNX模型文件路径 "output_dir": 输出目录路径 "device": (可选) 运行设备 ("cpu" 或 "gpu"),默认为 "gpu" """ # 从参数中提取值 input_dir = params["video_dir"] model_path = params["model"] output_dir = params["output_dir"] device = params.get("device", "gpu") # 确保输出目录存在 os.makedirs(output_dir, exist_ok=True) # 检查ffmpeg是否可用 if has_ffmpeg(): print("ffmpeg已安装,将自动为视频添加音频") else: print("警告: ffmpeg未安装,生成的视频将没有声音") # 初始化卡通化器 cartoonizer = Cartoonizer(model_path, device) model_name = os.path.basename(model_path).rsplit('.', 1)[0] # 支持的媒体格式 image_exts = ['.png', '.jpg', '.jpeg', '.bmp', '.tiff', '.webp'] video_exts = ['.mp4', '.avi', '.mov', '.mkv', '.flv', '.wmv'] # 处理所有文件 processed_files = [] for filename in os.listdir(input_dir): filepath = os.path.join(input_dir, filename) if not os.path.isfile(filepath): continue ext = os.path.splitext(filename)[1].lower() output_name = f"{os.path.splitext(filename)[0]}_{model_name}{ext}" output_path = os.path.join(output_dir, output_name) try: if ext in image_exts: print(f"\n处理图片: {filename}") result = cartoonizer.process_image(filepath, output_path) if result: processed_files.append(result) print(f"保存为: {output_name}") elif ext in video_exts: print(f"\n处理视频: {filename}") result = cartoonizer.process_video(filepath, output_path) if result: processed_files.append(result) print(f"保存为: {output_name}") except Exception as e: print(f"处理 {filename} 时出错: {str(e)}") print("\n处理完成。") print(f"共处理文件: {len(processed_files)}") return processed_files def check_video_format(path): """检查视频格式是否符合要求""" if not has_ffmpeg(): print("无法检查视频格式: ffmpeg未安装") return cmd = ['ffprobe', '-v', 'error', '-select_streams', 'v:0', '-show_entries', 'stream=codec_name,pix_fmt', '-of', 'csv=p=0', path] try: result = subprocess.run(cmd, capture_output=True, text=True) return result.stdout.strip() except Exception as e: return f"检查失败: {str(e)}" if __name__ == "__main__": # 构建参数字典 params = { "video_dir": r"E:\软件视频类型测试\1带货测试\成品\切片法区测试", "model": r"E:\python成品\15视频转绘\AnimeGANv3-1.1.0\AnimeGANv3-1.1.0\deploy\AnimeGANv3_Hayao_36.onnx", "output_dir": r"E:\软件视频类型测试\1带货测试\成品\成品", "device": "cpu" # 强制使用CPU } try: # 执行转换 results = videopic_to_new(params) # 打印结果 print("\n处理后的文件:") for res in results: print(f" - {res}") # 检查视频格式 if res.lower().endswith(('.mp4', '.avi', '.mov', '.mkv', '.flv', '.wmv')): print(f" 视频格式: {check_video_format(res)}") except Exception as e: print(f"\n发生严重错误: {str(e)}") import traceback traceback.print_exc() 问题是我cuda是11.8为什么读出来是6.5
06-13
D:\admin-project\guangxi-quan-vue3>npm run electron:build -- --debug > flowoutdemo@0.0.0 electron:build > set ELECTRON_MIRROR=https://github.com/electron/electron/releases/download/ && npm run build && electron-builder -c.extraMetadata.main=electron/main.js --debug > flowoutdemo@0.0.0 build > vite build vite v5.4.19 building for production... ✓ 136 modules transformed. dist/index.html 0.43 kB │ gzip: 0.29 kB dist/assets/index-fRN9Gjbe.css 3.24 kB │ gzip: 1.18 kB dist/assets/home-4Rnli7SO.css 7.87 kB │ gzip: 1.90 kB dist/assets/index-DWI3OzZM.js 92.14 kB │ gzip: 36.63 kB dist/assets/home-qQcH4Glo.js 199.11 kB │ gzip: 69.78 kB ✓ built in 1.28s cli.js build Build Commands: cli.js build Build [default] cli.js install-app-deps Install app deps cli.js node-gyp-rebuild Rebuild own native code cli.js create-self-signed-cert Create self-signed code signing cert for Windows apps cli.js start Run application in a development mode using electron-webpack Building: -m, -o, --mac, --macos Build for macOS, accepts target list (see https://goo.gl/5uHuzj). [array] -l, --linux Build for Linux, accepts target list (see https://goo.gl/4vwQad) [array] -w, --win, --windows Build for Windows, accepts target list (see https://goo.gl/jYsTEJ) [array] --x64 Build for x64 [boolean] --ia32 Build for ia32 [boolean] --armv7l Build for armv7l [boolean] --arm64 Build for arm64 [boolean] --universal Build for universal [boolean] --dir Build unpacked dir. Useful to test. [boolean] --prepackaged, --pd The path to prepackaged app (to pack in a distributable format) --projectDir, --project The path to project directory. Defaults to current working directory. -c, --config The path to an electron-builder config. Defaults to `electron-builder.yml` (or `json`, or `json5`, or `js`, or `ts`), see https://goo.gl/YFRJOM Publishing: -p, --publish Publish artifacts, see https://goo.gl/tSFycD [choices: "onTag", "onTagOrDraft", "always", "never", undefined] Other: --help Show help [boolean] --version Show version number [boolean] Examples: electron-builder -mwl build for macOS, Windows and Linux electron-builder --linux deb tar.xz build deb and tar.xz for Linux electron-builder --win --ia32 build for Windows ia32 electron-builder set package.json property `foo` to -c.extraMetadata.foo=bar `bar` electron-builder configure unicode options for NSIS --config.nsis.unicode=false See https://electron.build for more documentation. Unknown argument: debug
06-07
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值