Functions/GenXdev.AI.Queries/Invoke-ImageObjectsUpdate.ps1
############################################################################### <# .SYNOPSIS Updates object detection metadata for image files in a specified directory. .DESCRIPTION This function processes images in a specified directory to detect objects using artificial intelligence. It creates JSON metadata files containing detected objects, their positions, confidence scores, and labels. The function supports batch processing with configurable confidence thresholds and can optionally skip existing metadata files or retry previously failed detections. .PARAMETER ImageDirectories The directory path containing images to process. Can be relative or absolute path. Default is the current directory. .PARAMETER Recurse If specified, processes images in the specified directory and all subdirectories recursively. .PARAMETER OnlyNew If specified, only processes images that don't already have object metadata files or have empty metadata files. .PARAMETER RetryFailed If specified, retries processing previously failed images that have empty metadata files or contain error indicators. .PARAMETER NoDockerInitialize Skip Docker initialization when this switch is used. Used when already called by parent function to avoid redundant container setup. .PARAMETER ConfidenceThreshold Minimum confidence threshold (0.0-1.0) for object detection. Objects detected with confidence below this threshold will be filtered out. Default is 0.5. .PARAMETER Force Force rebuild of Docker container and remove existing data when this switch is used. This will recreate the entire detection environment. .PARAMETER UseGPU Use GPU-accelerated version when this switch is used. Requires an NVIDIA GPU with appropriate drivers and CUDA support. .PARAMETER ContainerName The name for the Docker container running the object detection service. Default is "deepstack_face_recognition". .PARAMETER VolumeName The name for the Docker volume for persistent storage of detection models and data. Default is "deepstack_face_data". .PARAMETER ServicePort The port number for the DeepStack service to listen on. Must be between 1 and 65535. Default is 5000. .PARAMETER HealthCheckTimeout Maximum time in seconds to wait for service health check before timing out. Must be between 10 and 300 seconds. Default is 60. .PARAMETER HealthCheckInterval Interval in seconds between health check attempts when waiting for service startup. Must be between 1 and 10 seconds. Default is 3. .PARAMETER ImageName Custom Docker image name to use instead of the default DeepStack image. Allows using alternative object detection models or configurations. .EXAMPLE Invoke-ImageObjectsUpdate -ImageDirectories "C:\Photos" -Recurse This example processes all images in C:\Photos and all subdirectories using default settings with 0.5 confidence threshold. .EXAMPLE Invoke-ImageObjectsUpdate "C:\Photos" -RetryFailed -OnlyNew This example processes only new images and retries previously failed ones in the C:\Photos directory using positional parameter syntax. .EXAMPLE Invoke-ImageObjectsUpdate -ImageDirectories "C:\Photos" -UseGPU ` -ConfidenceThreshold 0.7 This example uses GPU acceleration with higher confidence threshold of 0.7 for more accurate but fewer object detections. #> function Invoke-ImageObjectsUpdate { [CmdletBinding()] [Alias('objectdetection')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] param( ####################################################################### [Parameter( Position = 0, Mandatory = $false, HelpMessage = 'The directory path containing images to process' )] [string] $ImageDirectories = '.\', ####################################################################### [Parameter( Mandatory = $false, HelpMessage = ('Process images in specified directory and all ' + 'subdirectories') )] [switch] $Recurse, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = ("Only process images that don't already have face " + 'metadata files') )] [switch] $OnlyNew, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = ('Will retry previously failed image keyword ' + 'updates') )] [switch] $RetryFailed, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = ('The language for generated descriptions and ' + 'keywords') )] [PSDefaultValue(Value = 'English')] [ValidateSet( 'Afrikaans', 'Akan', 'Albanian', 'Amharic', 'Arabic', 'Armenian', 'Azerbaijani', 'Basque', 'Belarusian', 'Bemba', 'Bengali', 'Bihari', 'Bosnian', 'Breton', 'Bulgarian', 'Cambodian', 'Catalan', 'Cherokee', 'Chichewa', 'Chinese (Simplified)', 'Chinese (Traditional)', 'Corsican', 'Croatian', 'Czech', 'Danish', 'Dutch', 'English', 'Esperanto', 'Estonian', 'Ewe', 'Faroese', 'Filipino', 'Finnish', 'French', 'Frisian', 'Ga', 'Galician', 'Georgian', 'German', 'Greek', 'Guarani', 'Gujarati', 'Haitian Creole', 'Hausa', 'Hawaiian', 'Hebrew', 'Hindi', 'Hungarian', 'Icelandic', 'Igbo', 'Indonesian', 'Interlingua', 'Irish', 'Italian', 'Japanese', 'Javanese', 'Kannada', 'Kazakh', 'Kinyarwanda', 'Kirundi', 'Kongo', 'Korean', 'Krio (Sierra Leone)', 'Kurdish', 'Kurdish (Soranî)', 'Kyrgyz', 'Laothian', 'Latin', 'Latvian', 'Lingala', 'Lithuanian', 'Lozi', 'Luganda', 'Luo', 'Macedonian', 'Malagasy', 'Malay', 'Malayalam', 'Maltese', 'Maori', 'Marathi', 'Mauritian Creole', 'Moldavian', 'Mongolian', 'Montenegrin', 'Nepali', 'Nigerian Pidgin', 'Northern Sotho', 'Norwegian', 'Norwegian (Nynorsk)', 'Occitan', 'Oriya', 'Oromo', 'Pashto', 'Persian', 'Polish', 'Portuguese (Brazil)', 'Portuguese (Portugal)', 'Punjabi', 'Quechua', 'Romanian', 'Romansh', 'Runyakitara', 'Russian', 'Scots Gaelic', 'Serbian', 'Serbo-Croatian', 'Sesotho', 'Setswana', 'Seychellois Creole', 'Shona', 'Sindhi', 'Sinhalese', 'Slovak', 'Slovenian', 'Somali', 'Spanish', 'Spanish (Latin American)', 'Sundanese', 'Swahili', 'Swedish', 'Tajik', 'Tamil', 'Tatar', 'Telugu', 'Thai', 'Tigrinya', 'Tonga', 'Tshiluba', 'Tumbuka', 'Turkish', 'Turkmen', 'Twi', 'Uighur', 'Ukrainian', 'Urdu', 'Uzbek', 'Vietnamese', 'Welsh', 'Wolof', 'Xhosa', 'Yiddish', 'Yoruba', 'Zulu')] [string] $Language, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = ('Skip LM-Studio initialization (used when already ' + 'called by parent function)') )] [switch] $NoLMStudioInitialize, ######################################################################## [Parameter( Mandatory = $false, HelpMessage = 'Force stop LM Studio before initialization' )] [switch]$Force, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = 'The type of LLM query' )] [ValidateSet( 'SimpleIntelligence', 'Knowledge', 'Pictures', 'TextTranslation', 'Coding', 'ToolUse' )] [string] $LLMQueryType = 'SimpleIntelligence', ######################################################################## [Parameter( Mandatory = $false, HelpMessage = 'The model identifier or pattern to use for AI operations' )] [string] $Model, ######################################################################## [Parameter( Mandatory = $false, HelpMessage = 'The LM Studio specific model identifier' )] [Alias('ModelLMSGetIdentifier')] [string] $HuggingFaceIdentifier, ######################################################################## [Parameter( Mandatory = $false, HelpMessage = 'The maximum number of tokens to use in AI operations' )] [int] $MaxToken, ######################################################################## [Parameter( Mandatory = $false, HelpMessage = 'The number of CPU cores to dedicate to AI operations' )] [int] $Cpu, ######################################################################## [Parameter( Mandatory = $false, HelpMessage = 'The time-to-live in seconds for cached AI responses' )] [int] $TTLSeconds, ######################################################################## [Parameter( Mandatory = $false, HelpMessage = 'The timeout in seconds for AI operations' )] [int] $TimeoutSeconds, ######################################################################## [Parameter( Mandatory = $false, HelpMessage = 'Database path for preference data files' )] [Alias('DatabasePath')] [string] $PreferencesDatabasePath, ######################################################################## [Parameter( Mandatory = $false, HelpMessage = 'Show LM Studio window during initialization' )] [switch]$ShowWindow, ######################################################################## [Alias('m', 'mon')] [parameter( Mandatory = $false, HelpMessage = 'The monitor to use, 0 = default, -1 is discard' )] [int] $Monitor = -1, ######################################################################## [Alias('nb')] [parameter( Mandatory = $false, HelpMessage = 'Removes the borders of the window' )] [switch] $NoBorders, ######################################################################## [parameter( Mandatory = $false, HelpMessage = 'The initial width of the window' )] [int] $Width = -1, ######################################################################## [parameter( Mandatory = $false, HelpMessage = 'The initial height of the window' )] [int] $Height = -1, ######################################################################## [parameter( Mandatory = $false, HelpMessage = 'The initial X position of the window' )] [int] $X = -1, ######################################################################## [parameter( Mandatory = $false, HelpMessage = 'The initial Y position of the window' )] [int] $Y = -1, ######################################################################## [parameter( Mandatory = $false, HelpMessage = 'Place window on the left side of the screen' )] [switch] $Left, ######################################################################## [parameter( Mandatory = $false, HelpMessage = 'Place window on the right side of the screen' )] [switch] $Right, ######################################################################## [parameter( Mandatory = $false, HelpMessage = 'Place window on the top side of the screen' )] [switch] $Top, ######################################################################## [parameter( Mandatory = $false, HelpMessage = 'Place window on the bottom side of the screen' )] [switch] $Bottom, ######################################################################## [parameter( Mandatory = $false, HelpMessage = 'Place window in the center of the screen' )] [switch] $Centered, ######################################################################## [Parameter( Mandatory = $false, HelpMessage = 'Sends F11 to the window' )] [Alias('fs')] [switch]$FullScreen, ######################################################################## [parameter( Mandatory = $false, HelpMessage = 'Restore PowerShell window focus' )] [Alias('rf', 'bg')] [switch]$RestoreFocus, ######################################################################## [parameter( Mandatory = $false, HelpMessage = 'Returns the window helper for each process' )] [Alias('pt')] [switch]$PassThru, ######################################################################## [parameter( Mandatory = $false, HelpMessage = 'Will either set the window fullscreen on a different monitor than Powershell, or ' + 'side by side with Powershell on the same monitor' )] [Alias('sbs')] [switch]$SideBySide, ############################################################################### [Parameter( Mandatory = $false, HelpMessage = 'Focus the window after opening' )] [Alias('fw','focus')] [switch] $FocusWindow, ############################################################################### [Parameter( Mandatory = $false, HelpMessage = 'Set the window to foreground after opening' )] [Alias('fg')] [switch] $SetForeground, ############################################################################### [Parameter( Mandatory = $false, HelpMessage = 'Maximize the window after positioning' )] [switch] $Maximize, ########################################################################### [Parameter( Mandatory = $false, HelpMessage = ('Keystrokes to send to the Window, ' + 'see documentation for cmdlet GenXdev.Windows\Send-Key') )] [ValidateNotNullOrEmpty()] [string[]] $KeysToSend, ######################################################################## [Parameter( Mandatory = $false, HelpMessage = ('Use alternative settings stored in session for AI ' + 'preferences') )] [switch] $SessionOnly, ######################################################################## [Parameter( Mandatory = $false, HelpMessage = ('Clear alternative settings stored in session for AI ' + 'preferences') )] [switch] $ClearSession, ######################################################################## [Parameter( Mandatory = $false, HelpMessage = ('Store settings only in persistent preferences without ' + 'affecting session') )] [Alias('FromPreferences')] [switch] $SkipSession ######################################################################## ) begin { # convert the possibly relative path to an absolute path for reliable access $path = GenXdev.FileSystem\Expand-Path $ImageDirectories # ensure the target directory exists before proceeding with any operations if (-not [System.IO.Directory]::Exists($path)) { Microsoft.PowerShell.Utility\Write-Host ( "The directory '$path' does not exist." ) return } # output verbose information about the processing directory Microsoft.PowerShell.Utility\Write-Verbose ( "Processing images for object detection in directory: $path" ) } process { # retrieve all supported image files from the specified directory # applying recursion only if the recurse switch was provided Microsoft.PowerShell.Management\Get-ChildItem ` -Path "$path\*.jpg", "$path\*.jpeg", "$path\*.gif","$path\*.png" ` -Recurse:$Recurse ` -File ` -ErrorAction SilentlyContinue | Microsoft.PowerShell.Core\ForEach-Object { try { # store the full path to the current image for better readability $image = $PSItem.FullName # output verbose information about current image being processed Microsoft.PowerShell.Utility\Write-Verbose ( "Processing image for object detection: $image" ) # construct path for the metadata file $metadataFilePath = "$($image):objects.json" # check if a metadata file already exists for this image $fileExists = [System.IO.File]::Exists($metadataFilePath) # check if we have valid existing content $hasValidContent = $false if ($fileExists) { try { $content = [System.IO.File]::ReadAllText($metadataFilePath) $existingData = $content | Microsoft.PowerShell.Utility\ConvertFrom-Json $hasValidContent = $existingData.success } catch { # If JSON parsing fails, treat as invalid content $hasValidContent = $false } } # determine if we should process this image based on conditions if ((-not $OnlyNew) -or (-not $fileExists) -or (-not $hasValidContent)) { # obtain object detection data using ai detection technology $params = GenXdev.Helpers\Copy-IdenticalParamValues ` -BoundParameters $PSBoundParameters ` -FunctionName 'GenXdev.AI\Get-ImageDetectedObjects' ` -DefaultValues (Microsoft.PowerShell.Utility\Get-Variable ` -Scope Local -ErrorAction SilentlyContinue) $objectData = Get-ImageDetectedObjects ` @params ` -ImagePath $image $NoDockerInitialize = $true; # process the detection results into structured data format $processedData = if ($objectData -and $objectData.success -and $objectData.predictions) { # extract predictions array from detection results $predictions = $objectData.predictions # create array of object labels from predictions $objectLabels = $predictions | Microsoft.PowerShell.Core\ForEach-Object { $_.label } # group objects by label to get counts $objectCounts = $objectLabels | Microsoft.PowerShell.Utility\Group-Object ` -NoElement # construct structured data object with all metadata $data = @{ success = $true count = $predictions.Count objects = $objectLabels predictions = $predictions object_counts = @{} } # populate object counts from grouped results $objectCounts | Microsoft.PowerShell.Core\ForEach-Object { $data.object_counts[$_.Name] = $_.Count } $data } else { # create empty structure if no objects are detected @{ success = $true count = 0 objects = @() predictions = @() object_counts = @{} } } # convert processed data to json format for storage $objects = $processedData | Microsoft.PowerShell.Utility\ConvertTo-Json ` -Depth 20 ` -WarningAction SilentlyContinue # output verbose confirmation of detection analysis completion Microsoft.PowerShell.Utility\Write-Verbose ( "Received object detection analysis for: $image" ) try { # re-parse and compress json for consistent formatting $newContent = ($objects | Microsoft.PowerShell.Utility\ConvertFrom-Json | Microsoft.PowerShell.Utility\ConvertTo-Json ` -Compress ` -Depth 20 ` -WarningAction SilentlyContinue) # ensure proper empty structure format if ($newContent -eq ( '{"predictions":null,"count":0,"objects":[]}' )) { $newContent = ( '{"success":true,"count":0,"objects":[],' + '"predictions":[],"object_counts":{}}' ) } # save the processed object data to metadata file [System.IO.File]::WriteAllText( $metadataFilePath, $newContent ) # output verbose confirmation of successful save Microsoft.PowerShell.Utility\Write-Verbose ( "Successfully saved object metadata for: $image" ) } catch { # log any errors that occur during metadata processing Microsoft.PowerShell.Utility\Write-Verbose ( "$PSItem`r`n$objects" ) } } } catch { # log any errors that occur during image processing Microsoft.PowerShell.Utility\Write-Verbose ( "Error processing image '$image': " + "$($_.Exception.Message)" ) } } } end { } } |