Enhance Face Recognition Workflow with Remote Capabilities and Consent Management
All checks were successful
Publish FaceAI Container / publish (push) Successful in 5m20s
All checks were successful
Publish FaceAI Container / publish (push) Successful in 5m20s
- Updated `run_face_encoder.bat` to include remote execution parameters for SSH and SCP. - Refactored `run_face_encoder.ps1` to accept remote execution parameters and handle remote file operations. - Modified `FaceAiUploadPanel.vue` to introduce consent management UI and error handling for race availability. - Enhanced `useFaceAiHome.js` to manage consent acceptance and integrate cookie handling for biometric data processing notice. - Updated `HomeView.vue` to streamline the upload panel and integrate consent handling logic. Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
parent
dd7d4c865b
commit
6e37aa16c8
6 changed files with 1163 additions and 129 deletions
|
|
@ -1,21 +1,21 @@
|
|||
# --- Selezione livello multicore ---
|
||||
Write-Host ""
|
||||
Write-Host "Seleziona il livello di multicore per l'elaborazione CPU:"
|
||||
Write-Host " 1 = 1/8 dei core"
|
||||
Write-Host " 2 = 1/4 dei core"
|
||||
Write-Host " 3 = 1/2 dei core (predefinito)"
|
||||
Write-Host " 4 = 3/4 dei core"
|
||||
Write-Host " 5 = n-2 core"
|
||||
Write-Host ""
|
||||
$multicoreChoice = Read-Host "Inserisci il livello (1-5) oppure premi Invio per usare il predefinito (3)"
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string]$RemoteUser,
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string]$RemoteHost,
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string]$RemotePort,
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string]$SshExe,
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string]$ScpExe
|
||||
)
|
||||
|
||||
if ($multicoreChoice -match '^[1-5]$') {
|
||||
$multicore = [int]$multicoreChoice
|
||||
} else {
|
||||
$multicore = -1
|
||||
}
|
||||
|
||||
# --- Modern folder picker (IFileOpenDialog, Vista+) ---
|
||||
Add-Type -TypeDefinition @'
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
|
@ -87,63 +87,369 @@ public class ModernFolderPicker {
|
|||
}
|
||||
'@
|
||||
|
||||
$inputPath = [ModernFolderPicker]::Show("Select the folder containing images to encode")
|
||||
Add-Type -AssemblyName System.Windows.Forms
|
||||
$remoteRoots = @(
|
||||
'/mnt/nas12/nas2/RUS',
|
||||
'/mnt/da1/foto'
|
||||
)
|
||||
$encoderDir = Join-Path $PSScriptRoot 'face_encoder_cpu'
|
||||
$outputDir = Join-Path $encoderDir 'output'
|
||||
|
||||
if (-not $inputPath) {
|
||||
Write-Host "No folder selected. Exiting."
|
||||
exit 0
|
||||
function ConvertTo-PosixSingleQuoted {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Value
|
||||
)
|
||||
|
||||
return "'" + $Value.Replace("'", "'`"'`"'") + "'"
|
||||
}
|
||||
|
||||
$inputFolder = Get-Item -LiteralPath $inputPath -ErrorAction Stop
|
||||
$raceName = $inputFolder.Name
|
||||
$safeRaceName = ($raceName -replace '[<>:"/\\|?*]', ' ').Trim()
|
||||
$safeRaceName = $safeRaceName -replace '\s+', '_'
|
||||
if (-not $safeRaceName) {
|
||||
$safeRaceName = 'race'
|
||||
function Normalize-RemoteRelativePath {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$InputPath
|
||||
)
|
||||
|
||||
$normalized = $InputPath.Trim()
|
||||
$normalized = $normalized -replace '\\', '/'
|
||||
$normalized = $normalized -replace '^/mnt/nas12/nas2/RUS/?', ''
|
||||
$normalized = $normalized -replace '^/mnt/da1/foto/?', ''
|
||||
$normalized = $normalized -replace '/+', '/'
|
||||
$normalized = $normalized.Trim('/')
|
||||
|
||||
if (-not $normalized) {
|
||||
throw 'Il percorso remoto non puo essere vuoto.'
|
||||
}
|
||||
|
||||
$segments = $normalized -split '/'
|
||||
foreach ($segment in $segments) {
|
||||
if ([string]::IsNullOrWhiteSpace($segment)) {
|
||||
throw 'Il percorso remoto contiene segmenti vuoti non validi.'
|
||||
}
|
||||
|
||||
if ($segment -in @('.', '..')) {
|
||||
throw 'Il percorso remoto non puo contenere . o ...'
|
||||
}
|
||||
}
|
||||
|
||||
return ($segments -join '/')
|
||||
}
|
||||
|
||||
$timestamp = Get-Date -Format 'yyyyMMdd_HHmmss'
|
||||
$encoderDir = Join-Path $PSScriptRoot "face_encoder_cpu"
|
||||
$outputDir = Join-Path $encoderDir "output"
|
||||
$outputFile = Join-Path $outputDir ("face_encodings_{0}_{1}.pkl" -f $timestamp, $safeRaceName)
|
||||
$logFile = Join-Path $outputDir ("encoder_log_{0}_{1}.txt" -f $timestamp, $safeRaceName)
|
||||
function Join-RemotePath {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Root,
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$RelativePath,
|
||||
[string]$LeafName
|
||||
)
|
||||
|
||||
New-Item -ItemType Directory -Path $outputDir -Force | Out-Null
|
||||
$parts = @($Root.TrimEnd('/'))
|
||||
if ($RelativePath) {
|
||||
$parts += $RelativePath.Trim('/')
|
||||
}
|
||||
if ($LeafName) {
|
||||
$parts += $LeafName
|
||||
}
|
||||
|
||||
# --- Build argument list ---
|
||||
$encoderExe = Join-Path $encoderDir "face_encoder_cpu.exe"
|
||||
|
||||
$encoderArgs = [System.Collections.Generic.List[string]]::new()
|
||||
$encoderArgs.Add("-i")
|
||||
$encoderArgs.Add($inputFolder.FullName)
|
||||
$encoderArgs.Add("-r")
|
||||
$encoderArgs.Add("-o")
|
||||
$encoderArgs.Add($outputFile)
|
||||
$encoderArgs.Add("-l")
|
||||
$encoderArgs.Add($logFile)
|
||||
|
||||
if ($multicore -ge 0) {
|
||||
$encoderArgs.Add("-m")
|
||||
$encoderArgs.Add([string]$multicore)
|
||||
return ($parts -join '/')
|
||||
}
|
||||
|
||||
# --- Run encoder ---
|
||||
Write-Host "Input folder : $($inputFolder.FullName)"
|
||||
Write-Host "Race name : $raceName"
|
||||
Write-Host "Multicore : $(if ($multicore -ge 0) { $multicore } else { 'default (3)' })"
|
||||
Write-Host "Output file : $outputFile"
|
||||
Write-Host "Log file : $logFile"
|
||||
Write-Host "Command : $encoderExe $encoderArgs"
|
||||
Write-Host ""
|
||||
function Get-MulticoreSetting {
|
||||
Write-Host ''
|
||||
Write-Host "Seleziona il livello di multicore per l'elaborazione CPU:"
|
||||
Write-Host ' 1 = 1/8 dei core'
|
||||
Write-Host ' 2 = 1/4 dei core'
|
||||
Write-Host ' 3 = 1/2 dei core (predefinito)'
|
||||
Write-Host ' 4 = 3/4 dei core'
|
||||
Write-Host ' 5 = n-2 core'
|
||||
Write-Host ''
|
||||
|
||||
& $encoderExe @encoderArgs
|
||||
$multicoreChoice = Read-Host 'Inserisci il livello (1-5) oppure premi Invio per usare il predefinito (3)'
|
||||
if ($multicoreChoice -match '^[1-5]$') {
|
||||
return [int]$multicoreChoice
|
||||
}
|
||||
|
||||
$encoderExitCode = $LASTEXITCODE
|
||||
|
||||
if ($encoderExitCode -eq 0 -and (Test-Path -LiteralPath $outputFile)) {
|
||||
Start-Process explorer.exe "/select,`"$outputFile`""
|
||||
} elseif ($encoderExitCode -eq 0) {
|
||||
Write-Warning "Encoding completed, but the expected output file was not found: $outputFile"
|
||||
return -1
|
||||
}
|
||||
|
||||
exit $encoderExitCode
|
||||
function Show-PklFilePicker {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$InitialDirectory
|
||||
)
|
||||
|
||||
$dialog = New-Object System.Windows.Forms.OpenFileDialog
|
||||
$dialog.Title = 'Seleziona il file PKL da caricare'
|
||||
$dialog.Filter = 'Pickle files (*.pkl)|*.pkl|Tutti i file (*.*)|*.*'
|
||||
$dialog.CheckFileExists = $true
|
||||
$dialog.Multiselect = $false
|
||||
|
||||
if (Test-Path -LiteralPath $InitialDirectory) {
|
||||
$dialog.InitialDirectory = (Resolve-Path -LiteralPath $InitialDirectory).Path
|
||||
}
|
||||
|
||||
if ($dialog.ShowDialog() -ne [System.Windows.Forms.DialogResult]::OK) {
|
||||
return $null
|
||||
}
|
||||
|
||||
return $dialog.FileName
|
||||
}
|
||||
|
||||
function Invoke-RemoteShellCommand {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Payload,
|
||||
[switch]$AllocateTty,
|
||||
[switch]$Quiet
|
||||
)
|
||||
|
||||
$remoteCommand = 'sh -c ' + (ConvertTo-PosixSingleQuoted $Payload)
|
||||
$sshArgs = [System.Collections.Generic.List[string]]::new()
|
||||
if ($AllocateTty) {
|
||||
$sshArgs.Add('-tt')
|
||||
}
|
||||
$sshArgs.Add('-p')
|
||||
$sshArgs.Add($remotePort)
|
||||
$sshArgs.Add('-o')
|
||||
$sshArgs.Add('PreferredAuthentications=password')
|
||||
$sshArgs.Add('-o')
|
||||
$sshArgs.Add('PubkeyAuthentication=no')
|
||||
$sshArgs.Add('-o')
|
||||
$sshArgs.Add('StrictHostKeyChecking=accept-new')
|
||||
$sshArgs.Add("$remoteUser@$remoteHost")
|
||||
$sshArgs.Add($remoteCommand)
|
||||
|
||||
if ($Quiet) {
|
||||
& $sshExe @sshArgs | Out-Null
|
||||
} else {
|
||||
& $sshExe @sshArgs
|
||||
}
|
||||
|
||||
return $LASTEXITCODE
|
||||
}
|
||||
|
||||
function Test-RemoteFileExists {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$RemotePath
|
||||
)
|
||||
|
||||
$payload = 'test -e ' + (ConvertTo-PosixSingleQuoted $RemotePath)
|
||||
$exitCode = Invoke-RemoteShellCommand -Payload $payload -Quiet
|
||||
|
||||
if ($exitCode -eq 0) {
|
||||
return $true
|
||||
}
|
||||
|
||||
if ($exitCode -eq 1) {
|
||||
return $false
|
||||
}
|
||||
|
||||
throw "Impossibile verificare l'esistenza del file remoto: $RemotePath"
|
||||
}
|
||||
|
||||
function Test-RemoteDirectoryExists {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$RemotePath
|
||||
)
|
||||
|
||||
$payload = 'test -d ' + (ConvertTo-PosixSingleQuoted $RemotePath)
|
||||
$exitCode = Invoke-RemoteShellCommand -Payload $payload -Quiet
|
||||
|
||||
if ($exitCode -eq 0) {
|
||||
return $true
|
||||
}
|
||||
|
||||
if ($exitCode -eq 1) {
|
||||
return $false
|
||||
}
|
||||
|
||||
throw "Impossibile verificare il percorso remoto: $RemotePath"
|
||||
}
|
||||
|
||||
function Ensure-RemoteDirectory {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$RemoteDirectory
|
||||
)
|
||||
|
||||
$payload = 'mkdir -p ' + (ConvertTo-PosixSingleQuoted $RemoteDirectory)
|
||||
$exitCode = Invoke-RemoteShellCommand -Payload $payload
|
||||
if ($exitCode -ne 0) {
|
||||
throw "Creazione cartella remota non riuscita: $RemoteDirectory"
|
||||
}
|
||||
}
|
||||
|
||||
function Upload-FileToRemoteTarget {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$LocalFile,
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$RemoteFile,
|
||||
[bool]$Overwrite
|
||||
)
|
||||
|
||||
$remoteDirectory = Split-Path -Path $RemoteFile -Parent
|
||||
if ([string]::IsNullOrWhiteSpace($remoteDirectory) -or $remoteDirectory -eq '/') {
|
||||
throw 'Il percorso remoto destinazione non e valido.'
|
||||
}
|
||||
|
||||
if (Test-RemoteDirectoryExists -RemotePath $RemoteFile) {
|
||||
throw "La destinazione remota e una cartella, non un file: $RemoteFile"
|
||||
}
|
||||
|
||||
Ensure-RemoteDirectory -RemoteDirectory $remoteDirectory
|
||||
|
||||
if ((-not $Overwrite) -and (Test-RemoteFileExists -RemotePath $RemoteFile)) {
|
||||
Write-Host "Salto upload per file esistente: $RemoteFile"
|
||||
return
|
||||
}
|
||||
|
||||
$scpArgs = @(
|
||||
'-P', $remotePort,
|
||||
'-o', 'PreferredAuthentications=password',
|
||||
'-o', 'PubkeyAuthentication=no',
|
||||
'-o', 'StrictHostKeyChecking=accept-new',
|
||||
$LocalFile,
|
||||
("{0}@{1}:{2}" -f $remoteUser, $remoteHost, (ConvertTo-PosixSingleQuoted $RemoteFile))
|
||||
)
|
||||
|
||||
& $scpExe @scpArgs
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "Caricamento non riuscito verso $RemoteFile"
|
||||
}
|
||||
}
|
||||
|
||||
function Invoke-FaceEncoding {
|
||||
$multicore = Get-MulticoreSetting
|
||||
$inputPath = [ModernFolderPicker]::Show('Select the folder containing images to encode')
|
||||
|
||||
if (-not $inputPath) {
|
||||
Write-Host 'Nessuna cartella selezionata. Uscita.'
|
||||
return 0
|
||||
}
|
||||
|
||||
$inputFolder = Get-Item -LiteralPath $inputPath -ErrorAction Stop
|
||||
$raceName = $inputFolder.Name
|
||||
$safeRaceName = ($raceName -replace '[<>:"/\\|?*]', ' ').Trim()
|
||||
$safeRaceName = $safeRaceName -replace '\s+', '_'
|
||||
if (-not $safeRaceName) {
|
||||
$safeRaceName = 'race'
|
||||
}
|
||||
|
||||
$timestamp = Get-Date -Format 'yyyyMMdd_HHmmss'
|
||||
$outputFile = Join-Path $outputDir ("face_encodings_{0}_{1}.pkl" -f $timestamp, $safeRaceName)
|
||||
$logFile = Join-Path $outputDir ("encoder_log_{0}_{1}.txt" -f $timestamp, $safeRaceName)
|
||||
|
||||
New-Item -ItemType Directory -Path $outputDir -Force | Out-Null
|
||||
|
||||
$encoderExe = Join-Path $encoderDir 'face_encoder_cpu.exe'
|
||||
$encoderArgs = [System.Collections.Generic.List[string]]::new()
|
||||
$encoderArgs.Add('-i')
|
||||
$encoderArgs.Add($inputFolder.FullName)
|
||||
$encoderArgs.Add('-r')
|
||||
$encoderArgs.Add('-o')
|
||||
$encoderArgs.Add($outputFile)
|
||||
$encoderArgs.Add('-l')
|
||||
$encoderArgs.Add($logFile)
|
||||
|
||||
if ($multicore -ge 0) {
|
||||
$encoderArgs.Add('-m')
|
||||
$encoderArgs.Add([string]$multicore)
|
||||
}
|
||||
|
||||
Write-Host "Input folder : $($inputFolder.FullName)"
|
||||
Write-Host "Race name : $raceName"
|
||||
Write-Host "Multicore : $(if ($multicore -ge 0) { $multicore } else { 'default (3)' })"
|
||||
Write-Host "Output file : $outputFile"
|
||||
Write-Host "Log file : $logFile"
|
||||
Write-Host "Command : $encoderExe $encoderArgs"
|
||||
Write-Host ''
|
||||
|
||||
& $encoderExe @encoderArgs
|
||||
$encoderExitCode = $LASTEXITCODE
|
||||
|
||||
if ($encoderExitCode -eq 0 -and (Test-Path -LiteralPath $outputFile)) {
|
||||
Start-Process explorer.exe "/select,`"$outputFile`""
|
||||
} elseif ($encoderExitCode -eq 0) {
|
||||
Write-Warning "Encoding completed, but the expected output file was not found: $outputFile"
|
||||
}
|
||||
|
||||
return $encoderExitCode
|
||||
}
|
||||
|
||||
function Invoke-PklUpload {
|
||||
New-Item -ItemType Directory -Path $outputDir -Force | Out-Null
|
||||
|
||||
$selectedFile = Show-PklFilePicker -InitialDirectory $outputDir
|
||||
if (-not $selectedFile) {
|
||||
Write-Host 'Nessun file selezionato. Uscita.'
|
||||
return 0
|
||||
}
|
||||
|
||||
$fileInfo = Get-Item -LiteralPath $selectedFile -ErrorAction Stop
|
||||
$relativeInput = Read-Host 'Inserisci il percorso relativo sotto RUS/foto (esempio: 2026/04.APRILE/ISOLOTTO)'
|
||||
$relativePath = Normalize-RemoteRelativePath -InputPath $relativeInput
|
||||
|
||||
$targets = foreach ($root in $remoteRoots) {
|
||||
[pscustomobject]@{
|
||||
Root = $root
|
||||
RemoteFile = Join-RemotePath -Root $root -RelativePath $relativePath -LeafName $fileInfo.Name
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host ''
|
||||
Write-Host 'Destinazioni remote:'
|
||||
foreach ($target in $targets) {
|
||||
Write-Host (" - {0}" -f $target.RemoteFile)
|
||||
}
|
||||
Write-Host ''
|
||||
|
||||
$existingTargets = @($targets | Where-Object { Test-RemoteFileExists -RemotePath $_.RemoteFile })
|
||||
$overwrite = $false
|
||||
if ($existingTargets.Count -gt 0) {
|
||||
Write-Host 'Il file esiste gia nelle seguenti destinazioni:'
|
||||
foreach ($target in $existingTargets) {
|
||||
Write-Host (" - {0}" -f $target.RemoteFile)
|
||||
}
|
||||
|
||||
$choice = Read-Host 'Vuoi sovrascrivere i file esistenti? (s/N)'
|
||||
if ($choice -match '^(s|si|y|yes)$') {
|
||||
$overwrite = $true
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($target in $targets) {
|
||||
Upload-FileToRemoteTarget -LocalFile $fileInfo.FullName -RemoteFile $target.RemoteFile -Overwrite:$overwrite
|
||||
}
|
||||
|
||||
Write-Host ''
|
||||
Write-Host 'Caricamento completato.'
|
||||
return 0
|
||||
}
|
||||
|
||||
Write-Host ''
|
||||
Write-Host 'Seleziona un''opzione:'
|
||||
Write-Host ' 1. Elaborazione riconoscimento facciale'
|
||||
Write-Host ' 2. Caricamento'
|
||||
Write-Host ''
|
||||
|
||||
$mode = $null
|
||||
while ($mode -notin @('1', '2')) {
|
||||
$mode = Read-Host 'Inserisci 1 o 2'
|
||||
}
|
||||
|
||||
try {
|
||||
if ($mode -eq '1') {
|
||||
$exitCode = Invoke-FaceEncoding
|
||||
} else {
|
||||
$exitCode = Invoke-PklUpload
|
||||
}
|
||||
|
||||
exit $exitCode
|
||||
} catch {
|
||||
Write-Error $_
|
||||
exit 1
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue