Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- # Path to ImageMagick executable, I used the portable version from https://www.imagemagick.org/script/download.php#windows
- $magick = "C:\Scripting\face-detection\tools\imagemagick\magick.exe"
- # You will need a free Azure account and you'll have to add the Face API from Azure Cognitive Services to it.
- # The free tier lets you do 20 transactions per minute and 30 000 free transactions per month.
- # Your API-key for the Face API.
- $facesApiKey = "Your API key"
- # 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.
- $facesEndpoint = "https://westeurope.api.cognitive.microsoft.com/face/v1.0"
- function Get-FaceData ([string] $imageFilePath){
- # This function is based on the quickstart for cURL at https://docs.microsoft.com/en-us/azure/cognitive-services/face/quickstarts/curl .
- # 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,
- # hence the different "Content-Type" header (I took this from the C# example).
- $faceDetectUrl = "$($facesEndpoint)/detect?returnFaceId=true&returnFaceLandmarks=false"
- $headers = @{"Content-Type" = "application/octet-stream"; "Ocp-Apim-Subscription-Key" = $facesApiKey}
- $faceResponse = Invoke-WebRequest -Method POST -Headers $headers -InFile $imageFilePath -Uri $faceDetectUrl
- $faceResponse
- }
- function Get-ImageWidth($imageInputPath){
- $imageWidthStr = (&$magick identify -format "%w" $imageInputPath)
- $imageWidth = [int]::Parse($imageWidthStr)
- $imageWidth
- }
- function Get-ImageHeight($imageInputPath){
- $imageHeightStr = (&$magick identify -format "%h" $imageInputPath)
- $imageHeight = [int]::Parse($imageHeightStr)
- $imageHeight
- }
- function Prepare-Image ($imageInputPath, $tempFolder){
- $pathToReturn = $imageInputPath
- $item = Get-Item $imageInputPath
- # 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.
- if ($item.Length -gt 4MB){
- $tempImagePath = Join-Path $tempFolder (Split-Path -Path $imageInputPath -Leaf)
- # 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.
- # "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.
- # The argument given to the resize parameter is an ImageMagick geometry argument: https://www.imagemagick.org/script/command-line-processing.php#geometry
- &$magick $imageInputPath -auto-orient -resize "2000x2000" $tempImagePath
- $pathToReturn = $tempImagePath
- }
- $pathToReturn
- }
- function Process-Image ($imageInputPath, $imageOutputPath, [double] $cropPercentH, [double] $cropPercentV, [string] $outputGeometry, [bool] $desaturate) {
- # 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
- $faceData = Get-FaceData($imageInputPath)
- $face = $faceData.Content | ConvertFrom-Json
- $imageWidth = Get-ImageWidth $imageInputPath
- $imageHeight = Get-ImageHeight $imageInputPath
- # We are going to determine the width and the height of the rectangle that we are going to crop out of the image.
- # 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)
- # Same thing for the height.
- # 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.
- # If you put the output files next to each other horizontally, you'll see that everyone's eyes are on the same line.
- $cropWidth = [math]::Floor($face.faceRectangle.width + ($face.faceRectangle.width * $cropPercentageH * 2))
- $cropHeight = [math]::Floor($face.faceRectangle.height + ($face.faceRectangle.height * $cropPercentageV * 2))
- # Determine where the center of the face is in the image
- $centerOfFaceX = [math]::Floor($face.faceRectangle.left + ($face.faceRectangle.width/2))
- $centerOfFaceY = [math]::Floor($face.faceRectangle.top + ($face.faceRectangle.height/2))
- # 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
- $offsetX = [math]::Floor($centerOfFaceX - ($cropWidth / 2))
- $offsetY = [math]::Floor($centerOfFaceY - ($cropHeight / 2))
- if ($offsetX -lt 0) {
- Write-Warning "Not enough space to the left of the face in the input image to ensure correct crop"
- }
- if (($centerOfFaceX + [math]::Floor($cropWidth/2)) -gt $imageWidth){
- Write-Warning "Not enough space to the right of the face in the input image to ensure correct crop"
- }
- if ($offsetY -lt 0){
- Write-Warning "Not enough space above the face in the input image to ensure correct crop"
- }
- if (($centerOfFaceY + [math]::Floor($cropHeight/2)) -gt $imageHeight){
- Write-Warning "Not enough space below the face in the input image to ensure correct crop"
- }
- if ($desaturate){
- # "strip" removes all EXIF-data from the output file, this is mostly there to just make the output files smaller
- # "normalize" increases the contrast a bit, making the image "pop" more ( https://www.imagemagick.org/script/command-line-options.php#normalize )
- # The argument given to the resize parameter is an ImageMagick geometry argument: https://www.imagemagick.org/script/command-line-processing.php#geometry
- # 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
- &$magick convert $imageInputPath -auto-orient -crop "$($cropWidth)x$($cropHeight)+$offsetX+$offsetY" -resize "$($outputGeometry)" -strip -normalize -colorspace "Gray" $imageOutputPath
- } else {
- &$magick convert $imageInputPath -auto-orient -crop "$($cropWidth)x$($cropHeight)+$offsetX+$offsetY" -resize "$($outputGeometry)" -strip -normalize $imageOutputPath
- }
- }
- # Percentage of the width of the face that should be added to the left and right of the face to determine the crop rectangle
- $cropPercentageH = 0.80
- # Percentage of the height of the face that should be added to the top and bottom of the face to determine the crop rectangle
- $cropPercentageV = 0.80
- $imagesInputFolder = "C:\Scripting\face-detection\input-images\"
- $imagesOutputFolder = "C:\Scripting\face-detection\output-images\"
- $imagesTempFolder = "C:\Scripting\face-detection\temp"
- $images = Get-ChildItem -Path $imagesInputFolder -filter *.jpg
- foreach ($image in $images){
- # Run the image through the prepare function to ensure we send an image with the appropriate filesize to the Faces API
- $inputImagePath = Prepare-Image $image.FullName $imagesTempFolder
- # Determine path for output file
- $outputImagePath = Join-Path $imagesOutputFolder (Split-Path -Path $inputImagePath -Leaf)
- Write-Host $inputImagePath
- # Run the image through the processing function, resize it proportionally to 400 pixels wide and desaturate it
- Process-Image $inputImagePath $outputImagePath $cropPercentageH $cropPercentageV "400" $true
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement