Powershell – Läs RGB information ur bilder

Har nu skrivit en ny funktion till vår hemautomationsmodul till Powershell.

ELpatron sett från dlink kameran
Bildanalysering med Powershell.

Den nya funktionen ”Read-MJ-ImageRGBPixel” returnerar information om pixlars färger i en bild. Vad är det som är så bra med detta? Nu är det möjliggör att trigga / övervaka ”visuellt” med script!

Med vilken billig webbkamera som helt och Powershell går det nu att läsa information ur bilder. Detta gör att webbkameror kan trigga händelser i olika system genom att ”se färg” och inte bara agera på ”rörelse”. Nyttjar den själv till både Vera och Telldus Znet.

Varför?

(För att det går!) Orsaken till att jag skrev funktionen var för att ”se” om lysdioden på elpatronen lyste via en webbkamera. Detta ville jag sedan få in i Vera på något enkelt sätt. Tyvärr fanns det inte något liknande till Powershell som fungerade för mig….

Hur fungerar det?

Funktionen jag skrev läser in en bild och översätter det till en bitmap, detta kan sedan analyseras. Genom att analyser X och Y koordinaterna i bilden där lysdioden befinner sig. pixelns färg avgör om jag ska aktivera / inaktivera något (I mitt fall Veran och Telldus Znet Lite). Nästa steg är att visa detta med NeoPixels på en Arduino eller ESP8266, typ som en led extender 🙂

Genom att köra ”help Read-Mj-ImageRGBPixel” (Efter att modulen / funktionen är installerad, se info längst ner på sidan) visas hur modulen fungerar och vilken typ av data som kan returneras.

Read-MJ-ImageRGBPixel
Help Read-MJ-ImageRGBPixel (Version 2)

Exempel

Exemplen nedan laddar ner en bilder från Dlink och Hikvision kameror, resultatet sparas i mappen C:\temp. Filen skickas sedan in i funktionen ”Read-MJ-ImageRGBPixel”. Funktionen läser vilken RGB färg som finns runt pixel  X430 och Y864 med en pixelarea på 4. Dvs den läser in pixlar i en fyrkant runt om pixeln jag vill veta för att få ett snitt på respektive färg.

# Rad för att analysera en enskild bild lokalt på datorn:
Read-MJ-ImageRGBPixel -imageURL C:\temp\demobild.jpg -PixelX 300 -PixelY 300 -PixelArea 5

# Rad för att hämta hem en bild från Dlink kameran med inloggning och sedan kontrollera informationen i en area.
Read-MJ-ImageRGBPixel -imageURL $((Get-MJ-WebCamImage -WebCam 10.20.30.40 -Destination C:\temp\ -username adminkonto -password Password -KameraModell Dlink).destination) -PixelX 430 -pixelY 864 -PixelArea 4

# Rad för att hämta hem en bild från Hikvision kamera med inloggning och sedan kontrollera information i en area.
Read-MJ-ImageRGBPixel -imageURL ((Get-MJ-WebCamImage -WebCam "KameraIP" -Destination C:\temp\ -KameraModell Hikvision -username ******** -password *********).Destination) -PixelX 430 -pixelY 864 -PixelArea 4

 

Vart finns X och Y koordinaten för pixeln i bilden?

För att lättast ta reda på vart pixeln finns i en bild, öppna bilden med paint (MSpaint.exe), håller markören över området/pixeln som ska avläsas med scriptet. Längst ner till vänster skrivs nu X och Y koordinater ut. Dessa anges sedan i scriptet.

MsPaint
Genom att hålla markören över ett område i MSPaint visas X och Y pixel koordinater. Vänster sida motsvarar X Höger sida motsvarar Y

 

Vad kan detta nyttjas till?

Finns det en bild på internet som innehåller information som du inte kan nå via text? Ladda hem bilden och analysera den med funktionen. Det går enkelt att analysera en större mängd bilder från övervakningskameror och ”se” när något flyttats. Detta kräver en del jobb, men det går!

Visar nu ett konkret exempel på hur jag nyttjar funktionen och  bäddar in detta i ett script . Resultatet nedan visar hur hur ofta elpatron gick under April.

Hur ofta kördes elpatronen den här månaden:

Med hjälp av bilder som sparats var 5:e minut dygnet runt under någon månads tid, kunde jag i efterhand beräkna någorlunda förbrukning. Bilderna som jag sparade hade namnformatet ”Pannrum – YYYY-MM-DD _ HH mm ss.jpg”. Jag fick indirekt koll på hur ofta pelletsbrännaren hade strulat med…

Först tog jag reda på vart i bilden X och Y koordinaterna fanns för lysdioderna ”3kW” och ”6kW” på elpatronen. Med hjälp av koordinaterna i bilderna kunde jag sedan se hur ofta dioderna lyste.

Innehållet i bilderna som analyseras är två röda lysdioder (markerade med en pil på bilderna nedan).

Elpatron sett från dlink kameran
Bilder: Vänster: El av, Mitten: 6kW, Höger : 3kW

Resultatet i Powershell färgkodades för att visuellt se att scriptet träffat rätt.

  • Grön text: medelvärdet på 3kW och 6kW överstiger inte 80  (dvs varken 3kW eller 6kW lyser)
  • Röd text: medelvärdet på 3kW överstiger 80, medans medelvärdet på 6kW ej överstiger 80 (dvs 3kW lyser)
  • Gul text: medelvärdet på 3kW överstiger ej 80, medan medelvärdet på 6kW överstiger 80 (dvs 6kW lyser)
Exempelel2
Grön text: värdet efter 3kW och 6kW överstiger inte 80 (dvs elpatronen var avstängd) Röd text: värdet efter 3kW överstiger 80, dvs 3kW patronen var aktiv. Gul text: värdet efter 6kW överstiger 80, dvs 6kW patronen var aktiv

Resultatet summerar längst ned i scriptet, vilket ger någorlunda koll på den beräknade förbrukningen bara genom att avläsa bilderna. Detta tog två tre minuter att gå igenom.

Exempelel

Resultatet av 7138 bilder verkar stämma relativt bra, tog lite stickprov och alla var korrekta. 24 dagar har 34560 minuter, dvs uträkningen blev inte helt 100, men avläsningen ur bilden är helt ok.

För att få resultatet ovan kördes följande script mot mappen där alla bilder fanns.

# Exempelscript på hur jag i efterhand bearbetar information ur ca 7000 bilder.  
[string]$MappMedFiler = "C:\Temp"

[int]$3KW_X = 237   # Pixel i bilden där dioden finns X
[int]$3KW_Y = 153   # Pixel i bilden där dioden finns Y

[int]$6KW_X = 228   # pixel i bilden där dioden finns X
[int]$6KW_Y = 154   # pixel i bilden där dioden finns Y

$antal3KW = 0      # 1 på räknaren motsvarar 5 minuter @ 3 KW 
$antal6KW = 0      # 1 på räknaren motsvarar 5 minuter @ 6 KW

$allfiles = Get-ChildItem -Path $MappMedFiler -Filter *.jpg  # laddar in alla filer  
$alladatum = @() 

Write-Host $allfiles.count 


foreach ($fil in $allfiles){

    $rgb3KW = Read-MJ-ImageRGBPixel -imageURL $($fil.fullname) -PixelX $3KW_X -pixelY $3KW_Y -PixelArea 2 
    
    $rgb6KW = Read-MJ-ImageRGBPixel -imageURL $($fil.fullname) -PixelX $6KW_X -pixelY $6KW_Y -PixelArea 2 

    $tid = ""

        if ($fil.basename -match "[a-zA-Z0-9]* - (?<Datum>[0-9]*-[0-9]*-[0-9]*) _ (?<Klockan>.*)")
        {
    
            $tid = "$($Matches.datum) - klockan $($Matches.Klockan.Replace(" ",":")) ingen el"
            
            $alladatum += [datetime]$Matches.datum
        } 
        else
        {
            Write-Host "FEL"
        }
    
    # Efter att ha kontrollerat några bilder såg jag att ett medelvärde (Red_Avg)alltid översteg 80 när någon av dioderna var igång. dioder var aldrig igång samtidigt. 

    if ($rgb3KW.Red_Avg -ge 80 -or $rgb6KW.Red_Avg -ge 80) 
    {
        if ($rgb3KW.Red_Avg -ge $rgb6KW.Red_Avg)
        {
            write-host "$tid - 3KW EL AKTIV - 3KW $($rgb3KW.Red_Avg) , 6KW $($rgb6KW.Red_Avg) - fil  $($fil.FullName)" -ForegroundColor Red
            $antal3KW++
        } 
        else 
        {
            write-host "$tid - 6KW EL AKTIV - 3KW $($rgb3KW.Red_Avg) , 6KW $($rgb6KW.Red_Avg) - fil  $($fil.FullName)" -ForegroundColor Yellow
            $antal6KW++
        }
    } 
    else 
    {
        write-host "$tid - EL AV   - 3KW $($rgb3KW.Red_Avg) , 6KW $($rgb6KW.Red_Avg) - fil  $($fil.FullName)" -ForegroundColor Green
    }

  
}

write-host "Gick igenom $($allfiles.count) bilder"
write-host "3kw - Totalt aktiv = $($antal3KW * 5) minuter"
write-host "6kw - Totalt aktiv = $($antal6KW * 5) minuter"
write-host "Elen var ej aktiv $($(($allfiles.count) - ($antal3KW + $antal6KW)) * 5) minuter"
"Antal dagar kontrollerade: $((($alladatum | Select-Object -Last 1) - ($alladatum | Select-Object -First 1) | Select-Object -ExpandProperty Days))"

 

Arduino

Genom att nyttja funktionen ”Send-MJ-ArduinoData” som finns i Powershell modulen, går det visuellt att återspegla dessa två lysdioder i realtid! Paketering och konvertering till en ESP8266 återstår (kommer snart mer info om detta med).

ArduinoOchNeoPixelOFF
Arduino – Ingen el aktiv

 

ArduinoOchNeoPixel
Arduino – 3kW aktiverad, dvs höger diod lyser rött.

 

Function Read-MJ-ImageRGBPixel

Vill du inte ladda hem och köra hela modulen utan bara är intresserad av den här funktionen så finns den här, om du spinner vidare på den eller dela den vidare ge gärna creds och länk till Automatiserar.se

<#
.Synopsis
   Skapat av Ispep 
   2016-04-20 
   Version 2
   www.automatiserar.se
   Funktion för att läsa fram färgen på en viss pixel i en bild.
.DESCRIPTION
   Genom att skicka in en url till en bild och X och Y värdet på en bild returneras ett objekt om bilden.
.EXAMPLE
   raden hämtar ut information om pixlarna X22 och Y33
   Read-MJ-ImageRGBPixel -imageURL C:\temp\DemoBild.jpg -PixelX 22 -pixelY 33

    ImageURL      : C:\Temp\DemoBild.jpg
    PixelX        : 22
    PixelY        : 33
    PixelArea     : 2
    Success       : True
    ImageWith     : 640
    ImageHeight   : 480
    Red           : 11
    Red_Max       : 13
    Red_Avg       : 10
    Red_Min       : 5
    Green         : 11
    Green_Max     : 13
    Green_Avg     : 10
    Green_Min     : 5
    Blue          : 11
    Blue_Max      : 13
    Blue_Avg      : 9
    Blue_Min      : 5
    ScriptVersion : 2

.EXAMPLE
   
#>
    [cmdletbinding()]
    param(
    [Parameter(Mandatory=$true)][string]$imageURL,
    [int]$PixelX,
    [int]$pixelY,
    [int]$PixelArea = 5 # Ange hur stor area runt som ska tas med.
    
    )


    begin
    {
        # Börjar skapa objektet som kommer att returneras
        $scriptversion = 2

        $PixelObjekt=[ordered]@{

        ImageURL      = $([string]$imageURL)
        PixelX        = $([int]$PixelX)
        PixelY        = $([int]$pixelY)
        PixelArea     = $([int]$PixelArea)
        Success       = $([bool]$true)
        ImageWith     = $([int])
        ImageHeight   = $([int])
        Red           = $([int])
        Red_Max       = $([int])
        Red_Avg       = $([int])
        Red_Min       = $([int])
        Green         = $([int])
        Green_Max     = $([int])
        Green_Avg     = $([int])
        Green_Min     = $([int])
        Blue          = $([int])       
        Blue_Max      = $([int])              
        Blue_Avg      = $([int])                 
        Blue_Min      = $([int])
        ScriptVersion = $([int]$scriptversion)
        
        }
        $ImageInfo = New-Object -TypeName psobject -Property $PixelObjekt

        if(!(Test-Path $($ImageInfo.imageURL))){Write-Warning "Kunde ej hitta bilden $($ImageInfo.ImageInfo)"; $ImageInfo.Success = $false;}
    }

    PROCESS{

        if ($ImageInfo.Success){
            Write-Verbose "$($MyInvocation.InvocationName):: Påbörjar inläsning av bild"
                
                Add-Type -AssemblyName System.Drawing
                $MyBitmapImage = [System.Drawing.Bitmap]::FromFile($ImageInfo.imageURL)

                $ImageInfo.ImageHeight = $MyBitmapImage.Height
                $ImageInfo.ImageWith   = $MyBitmapImage.Width

                # definierar max / min värdert som ska gås igenom
                $MinX = $PixelX - $PixelArea
                $MaxX = $pixelX + $PixelArea
                $Miny = $pixelY - $PixelArea
                $MaXy = $pixelY + $PixelArea
            
                Write-Verbose "$($MyInvocation.InvocationName):: MinX = $MinX, MaxX = $MaxX, MinY = $minY, MaxY = $MaXy"
            
                # Läser in bilden.

                if ($MaxX -le $MyBitmapImage.Width -and $MaXy -le $MyBitmapImage.Height -and $MinX -ge 0 -and $minY -ge 0)
                {
                    Write-Verbose "$($MyInvocation.InvocationName):: Pixlarna är inom bildens upplösning" 
                    
                        # om man är inom bilden körs detta.
                    $xValue = $MinX
                    $yValue = $Miny
                        $summa = while ($xValue -le $MaxX -and $yValue -le $MaXy){
        
                                        while ($xValue -le $MaxX)                                        
                                        {
        
                                            $MyBitmapImage.GetPixel($xValue,$yValue)
        
                                            $xValue++
                                        }
                                $xValue = $MinX
                                $yValue++
                                
                                }
                      
                      $tmpImage = $MyBitmapImage.GetPixel($PixelX, $PixelY)
                      $ImageInfo.Red     = [int]$tmpImage.r
                      $ImageInfo.Green   = [int]$tmpImage.g
                      $ImageInfo.Blue    = [int]$tmpImage.b
                        
                      $ImageInfo.Red_Avg   = [int]($summa.r | Measure-Object -Average | Select-Object -ExpandProperty Average)
                      $ImageInfo.Green_Avg = [int]($summa.g | Measure-Object -Average | Select-Object -ExpandProperty Average)
                      $ImageInfo.Blue_Avg  = [int]($summa.b | Measure-Object -Average | Select-Object -ExpandProperty Average)                      
                      $ImageInfo.Red_Max   = [int]($summa.r | Measure-Object -Maximum | Select-Object -ExpandProperty Maximum)
                      $ImageInfo.Green_Max = [int]($summa.g | Measure-Object -Maximum | Select-Object -ExpandProperty Maximum)
                      $ImageInfo.Blue_Max  = [int]($summa.b | Measure-Object -Maximum | Select-Object -ExpandProperty Maximum)
                      $ImageInfo.Red_Min   = [int]($summa.r | Measure-Object -Minimum | Select-Object -ExpandProperty Minimum)
                      $ImageInfo.Green_Min = [int]($summa.g | Measure-Object -Minimum | Select-Object -ExpandProperty Minimum)
                      $ImageInfo.Blue_Min  = [int]($summa.b | Measure-Object -Minimum | Select-Object -ExpandProperty Minimum)


                      $MyBitmapImage.Dispose()
                } 
                
                else  # Området du valt är utanför bilden

                {
                Write-Warning "Du har valt ett x / y värde som är utanför bilden"
                $ImageInfo.Success = $false

                }

        
        }
        else
        {
            Write-Verbose "$($MyInvocation.InvocationName):: Kunde inte hitta filen $($ImageInfo.imageinfo)"
            
        }
        
    }

    END
    {
            return $ImageInfo
               
    }


}

Vad behövs???

Vad behövs för att köra detta? En Dator med Windows 8.1 eller nyare (Jag har byggt den på Windows 10, så där fungerar den garanterat!). Utöver det behövs modulen laddas hem och placeras under ”C:\Program Files\WindowsPowerShell\Modules\Automatiserar” och namnges till ”Automatiserar.psm1”.

Modulen finns att hämta på vår Github

När detta är gjort startar du powershell.exe som administratör och skriver ”Set-ExecutionPolicy -ExecutionPolicy RemoteSigned”, detta tillåter script och liknande att köras lokalt på klienten. Stäng powershell och öppna det på nytt igen. Nu är modulen redo att användas!

Hoppas du hittar någon nytta med funktionen och modulen, har du någon bra idé på vad mer som skulle behövas i funktionen eller saknas skriv gärna det så vi får veta.

Gilla och dela gärna sidan så vi ser att det uppskattas det vi gör!

// Ispep

4 reaktioner på ”Powershell – Läs RGB information ur bilder”

  1. Häftigt!… Men vad händer om kameran rör på sig en smula? Får en liten knuff..
    Då blir det väl andra koordinater och skriptet ballar ur?

    1. precis, rör sig kameran så missar du informationen du söker (om du skickar en statisk pixel)
      Om den pixeln du söker rör sig skriver man helt enkelt en loop som anger ett område i bilden där det vill hitta finns inom, sedan kontrollerar man pixel för pixel inom området.

      Ska snart skriva ett sådant exempel som jag själv kör 🙂

      // Ispep

Lämna en kommentar

Din e-postadress kommer inte publiceras. Obligatoriska fält är märkta *