Advertisement
Guest User

Face detect and crop with Powershell

a guest
Oct 19th, 2017
273
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. # Path to ImageMagick executable, I used the portable version from https://www.imagemagick.org/script/download.php#windows
  2. $magick = "C:\Scripting\face-detection\tools\imagemagick\magick.exe"
  3.  
  4. # You will need a free Azure account and you'll have to add the Face API from Azure Cognitive Services to it.
  5. # The free tier lets you do 20 transactions per minute and 30 000 free transactions per month.
  6.  
  7. # Your API-key for the Face API.
  8. $facesApiKey = "Your API key"
  9.  
  10. # The endpoint for the Face API. This is dependent on the Azure region you used for Azure Cognitive Services in your Azure account, change it accordingly.
  11. $facesEndpoint = "https://westeurope.api.cognitive.microsoft.com/face/v1.0"
  12.  
  13.  
  14. function Get-FaceData ([string] $imageFilePath){    
  15.     # This function is based on the quickstart for cURL at https://docs.microsoft.com/en-us/azure/cognitive-services/face/quickstarts/curl .
  16.     # The only real difference here is that we send the image file in binary form whereas in the cURL-example it gets converted to ASCII first,
  17.     # hence the different "Content-Type" header (I took this from the C# example).
  18.  
  19.     $faceDetectUrl = "$($facesEndpoint)/detect?returnFaceId=true&returnFaceLandmarks=false"
  20.     $headers = @{"Content-Type" = "application/octet-stream"; "Ocp-Apim-Subscription-Key" = $facesApiKey}
  21.    
  22.     $faceResponse = Invoke-WebRequest -Method POST -Headers $headers -InFile $imageFilePath -Uri $faceDetectUrl
  23.     $faceResponse
  24. }
  25.  
  26. function Get-ImageWidth($imageInputPath){
  27.     $imageWidthStr = (&$magick identify -format "%w" $imageInputPath)
  28.     $imageWidth = [int]::Parse($imageWidthStr)
  29.     $imageWidth
  30. }
  31.  
  32. function Get-ImageHeight($imageInputPath){
  33.     $imageHeightStr = (&$magick identify -format "%h" $imageInputPath)
  34.     $imageHeight = [int]::Parse($imageHeightStr)
  35.     $imageHeight
  36. }
  37.  
  38. function Prepare-Image ($imageInputPath, $tempFolder){
  39.     $pathToReturn = $imageInputPath
  40.     $item = Get-Item $imageInputPath
  41.  
  42.     # Images sent to the Face API have to be 4MB or smaller, so we check the file size of the image and resize it a bit if it's larger than that.
  43.     if ($item.Length -gt 4MB){
  44.         $tempImagePath = Join-Path $tempFolder (Split-Path -Path $imageInputPath -Leaf)
  45.        
  46.         # If an image is shot in portrait mode, some cameras actually save it in landscape and set a flag in the image-file to indicate to image software that the image is supposed to be rotated.
  47.         # "auto-orient" instructs ImageMagick to actually rotate the pixels to the correct orientation according to this flag when resizing the image, otherwise this flag gets lost and the temp image has the wrong orientation.
  48.         # The argument given to the resize parameter is an ImageMagick geometry argument: https://www.imagemagick.org/script/command-line-processing.php#geometry
  49.         &$magick $imageInputPath -auto-orient -resize "2000x2000" $tempImagePath
  50.         $pathToReturn = $tempImagePath
  51.     }
  52.     $pathToReturn
  53. }
  54.  
  55. function Process-Image ($imageInputPath, $imageOutputPath, [double] $cropPercentH, [double] $cropPercentV, [string] $outputGeometry, [bool] $desaturate) {
  56.  
  57.     # Get the face data from the Face API. As I'm working with only portrait photos with one person in them I didn't write any code to handle photos with multiple people in them
  58.     $faceData = Get-FaceData($imageInputPath)
  59.     $face = $faceData.Content | ConvertFrom-Json
  60.  
  61.     $imageWidth = Get-ImageWidth $imageInputPath
  62.     $imageHeight = Get-ImageHeight $imageInputPath
  63.  
  64.     # We are going to determine the width and the height of the rectangle that we are going to crop out of the image.
  65.     # We take the width of the face rectangle and add it to this width multiplied by the horizontal crop percentage times 2 (times 2 so that we add some space on both the left and right of the face)
  66.     # Same thing for the height.
  67.     # This way, the faces in all our output images will have the same relative size while having approximately the same amount of "non-face space" around them.
  68.     # If you put the output files next to each other horizontally, you'll see that everyone's eyes are on the same line.
  69.     $cropWidth = [math]::Floor($face.faceRectangle.width + ($face.faceRectangle.width * $cropPercentageH * 2))
  70.     $cropHeight = [math]::Floor($face.faceRectangle.height + ($face.faceRectangle.height * $cropPercentageV * 2))
  71.  
  72.     # Determine where the center of the face is in the image
  73.     $centerOfFaceX = [math]::Floor($face.faceRectangle.left + ($face.faceRectangle.width/2))
  74.     $centerOfFaceY = [math]::Floor($face.faceRectangle.top + ($face.faceRectangle.height/2))
  75.  
  76.     # These values will be used to offset our crop rectangle so that the center of the face will be right in the center of the crop rectangle
  77.     $offsetX = [math]::Floor($centerOfFaceX - ($cropWidth / 2))
  78.     $offsetY = [math]::Floor($centerOfFaceY - ($cropHeight / 2))
  79.  
  80.     if ($offsetX -lt 0) {
  81.         Write-Warning "Not enough space to the left of the face in the input image to ensure correct crop"
  82.     }
  83.  
  84.     if (($centerOfFaceX + [math]::Floor($cropWidth/2)) -gt $imageWidth){
  85.         Write-Warning "Not enough space to the right of the face in the input image to ensure correct crop"
  86.     }
  87.  
  88.     if ($offsetY -lt 0){
  89.         Write-Warning "Not enough space above the face in the input image to ensure correct crop"        
  90.     }
  91.  
  92.     if (($centerOfFaceY + [math]::Floor($cropHeight/2)) -gt $imageHeight){
  93.         Write-Warning "Not enough space below the face in the input image to ensure correct crop"
  94.     }
  95.  
  96.  
  97.     if ($desaturate){
  98.         # "strip" removes all EXIF-data from the output file, this is mostly there to just make the output files smaller
  99.         # "normalize" increases the contrast a bit, making the image "pop" more ( https://www.imagemagick.org/script/command-line-options.php#normalize )
  100.         # The argument given to the resize parameter is an ImageMagick geometry argument: https://www.imagemagick.org/script/command-line-processing.php#geometry
  101.         # I didn't specify an image quality, this way ImageMagick will try to determine the quality setting of the input image and use the same setting for the output image
  102.  
  103.         &$magick convert $imageInputPath -auto-orient -crop "$($cropWidth)x$($cropHeight)+$offsetX+$offsetY" -resize "$($outputGeometry)" -strip -normalize -colorspace "Gray" $imageOutputPath        
  104.     } else {
  105.         &$magick convert $imageInputPath -auto-orient -crop "$($cropWidth)x$($cropHeight)+$offsetX+$offsetY" -resize "$($outputGeometry)" -strip -normalize $imageOutputPath
  106.     }        
  107. }
  108.  
  109. # Percentage of the width of the face that should be added to the left and right of the face to determine the crop rectangle
  110. $cropPercentageH = 0.80
  111. # Percentage of the height of the face that should be added to the top and bottom of the face to determine the crop rectangle
  112. $cropPercentageV = 0.80
  113.  
  114. $imagesInputFolder = "C:\Scripting\face-detection\input-images\"
  115. $imagesOutputFolder = "C:\Scripting\face-detection\output-images\"
  116. $imagesTempFolder = "C:\Scripting\face-detection\temp"
  117.  
  118. $images = Get-ChildItem -Path $imagesInputFolder -filter *.jpg
  119.  
  120. foreach ($image in $images){
  121.     # Run the image through the prepare function to ensure we send an image with the appropriate filesize to the Faces API
  122.     $inputImagePath = Prepare-Image $image.FullName $imagesTempFolder
  123.  
  124.     # Determine path for output file
  125.     $outputImagePath = Join-Path $imagesOutputFolder (Split-Path -Path $inputImagePath -Leaf)
  126.    
  127.     Write-Host $inputImagePath
  128.  
  129.     # Run the image through the processing function, resize it proportionally to 400 pixels wide and desaturate it
  130.     Process-Image $inputImagePath $outputImagePath $cropPercentageH $cropPercentageV "400" $true
  131. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement