Tag-arkiv: C#

Strukturel processering af billeder

Når billeder skal processeres programmatisk anvendes en matematisk metode der kaldes morfologiske transformationer (morphological transformations), mønster genkendelse (pattern recognition) og/eller egenskabs ekstraktion (feature extraction).

Her i juletiden har mange været påvirket af huller i asfalten langs eksempeltvist Vejle fjordbroen. Hvis vi begiver os ud i et tankeeksperiment er det med relativt simple billedbehandlingsalgoritmer “nemt” at detektere en betydelig andel af opbyggende huller og dermed kunne man forebyggende iværksætte udbedrende vejarbejde før et problem opstår.

Et eksempel kunne være anvendelse af thresholding, erosion og dilation på en stribe billeder taget af asfaltoverfladen på udvalgte vejstrækninger. For resten af denne artikel antages det, at sådanne billeder er tilgængelige.

Herunder vises en række billeder af asfaltoverflader hvor der er opstået minimale huller og sprækker, samt hvor alvorlige slaghuller forekommer og er delvist udbedrede. Ligeledes vises en normal asfalt overflade. Under hvert billede vises en morfologisk behandlet udgave, der har været underlagt den samme algoritme for samtlige billeder. Givet disse resultatbilleder er det relativt simpelt at afgøre hvorvidt et behandlet resultatbillede udgør en kandidat til manuel inspektion eller ej.

Klik på billedet for at se en stor udgave.

“Proof-of-concept” algoritmen benyttet på resultatbillederne herover er skrevet i .NET C#, og ser overordnet således ud:

Bitmap input = new Bitmap(filename);
Bitmap output =
  Dilate(
    Dilate(
      Threshold(
        Dilate(
          Dilate(
            Dilate(
              Dilate(
                Grayscale(input)
              )
            )
          )
        ), 70
      )
    )
  );

output.Save(
  Path.GetFileNameWithoutExtension(filename) +
  ".processed" + Path.GetExtension(filename));

Tankeeksperimentet skal naturligvis føres videre ud før det bliver praktisk anvendeligt.

Det er klart at der vil være tale om mange tusinde billeder og adskillige terrabytes data, hvorfor system arkitekturen bag processeringssystemet bør være baseret på en skalerbar multiserver/multikerne teknologi. Et eksempel på en gratis open-source arkitektur kunne være AMD’s Framewave eller nVidia’s CUDA.

Ligeledes vil der være særlige elementer i billederne der skal tages særlig hånd om, f.eks. vejstriber og overgange mellem forskellige belægningstyper med videre.

Udvidelses- og klassemetoder i C#

Jeg havde brug for at forklare forskellen på extension methods og almindelige klassemetoder idag, og det forbavsede mig i anledningen hvor længe siden det egentligt er jeg har brugt det selv i .NET sprog. Jeg bruger nemlig tit og ofte den “tilsvarende” mekanisme i JavaScript (f.eks. prototype nøgleordet).

Her er et eksempel på hvordan man kunne tælle alle ord i en streng ved brug af en almindelig klassemetode:

TestProgram.cs:

using System;
using System.Text;

namespace TestApplication
{
  public class Utilities 
  {
    public static int CountWords(String str)
    {
      return str.Split(new char[] { ' ', '.', '?' },
        StringSplitOptions.RemoveEmptyEntries).Length;
    }
  }

  class TestProgram
  {
    static void Main(string[] args)
    {
      String test = "Hello world, how are you doing?";

      Console.WriteLine(test);
      Console.WriteLine(Utilities.CountWords(test) + " words found.");
      Console.WriteLine("Press any key to quit.");
      Console.ReadKey();
    }
  }
}

Kørselsresultat:

Hello world, how are you doing?
6 words was found in the string.
Press any key to quit.

Metoden CountWords er her kun tilgængelig fra klassen Utilities. Men metoden er jo egentlig ret generel for alle instanser af String klassen, og det kunne nemt tænkes at den specielle funktion skal kaldes i fremtidige applikationer jeg laver, eller blot i andre klasser i samme applikation. Dertil kan man anvende extension methods til udvide de metoder der er tilgængelige i eksisterende klasser.

Extension methods, eller udvidelsesmetoder, implementeres ved at angive statiske metoder i en public static klasse. De klasser man har oprettet der indeholder udvidelsesmetoder skal så blot være i scopet der hvor man ønsker at anvende udvidelsesmetoderne. Dette sikres ved at benytte “using” direktivet for det namespace man har placeret klasserne for udvidelsesmetoderne i. Den første parameter i en udvidelsesmetode specificerer altid hviklen type (klasse) der udvides, og denne parameter skal altid indledes med en this modifikator, da det er denne modifikator sammen med static der får compileren til at genkende metoden som en udvidelsesmetode.   

Her et eksempel hvor Strings klassen får tilføjet en CountWords udvidelsesmetode:

MyExtensions.cs:

using System;
using System.Text;

namespace ExtensionMethods
{
  public static class MyExtensions
  {
    public static int CountWords(this String str)
    {
      return str.Split(new char[] { ' ', '.', '?' },
        StringSplitOptions.RemoveEmptyEntries).Length;
    }
  }
}

Den overstående klasse i namespacet ExtensionMethods udvider nu String klassen med metoden CountWords. Namespace navnet er ikke så vigtigt, det kan være hvad som helst faktisk. Udvidelsen af String klassen sker ved at this er angivet foran første parameter i CountWords metoden. Typen der udvides er String. Vi kan gøre brug af denne udvidelse ved at skrive “using ExtensionMethods;” i vores fremtidige applikationer, for eksempel:

TestProgram.cs:

using System;
using System.Text;
using MyExtensions;

namespace TestApplication
{
  class TestProgram
  {
    static void Main(string[] args)
    {
      String test = "Hello world, how are you doing?";

      Console.WriteLine(test);
      Console.WriteLine(test.CountWords() + " words found.");
      Console.WriteLine("Press any key to quit.");
      Console.ReadKey();
    }
  }
}

Kørselsresultat:

Hello world, how are you doing?
6 words was found in the string.
Press any key to quit.

Det er vigtigt at understrege at udvidelsen ikke finder sted på selve String klassen, men på instanser af String klassen. Helt konkret er det instansen str der modtages i selve CountWords metoden. Dette bevirker at man ikke kan lave klasse udvidelsesmetoder såsom “String.MyClassExtensionMethod” og tilsvarende. Ligeledes er adgangsniveauet for udvidelsesmetoder det samme som når man har med en given instans af en klasse at gøre. Man har således ikke adgang til protected og private metoder og egenskaber/variable på en klasse man udvider.

Microsoft Visual Studio’s Intellisense understøtter extension methods:

Der er lige et par huskeregler man bør have for øje:

  • Husk at udvidelsesmetoder kræver at der er anvendt et “using” direktiv.
  • Husk dermed at enten kildefilen eller DLL’en til ønskede udvidelsesmetoder er tilføjet projektet.
  • Endvidere skal man huske at eksisterende metoder med samme navn og signatur altid vinder over udvidelsesmetoder.
  • Tag ikke udvidelsesmetoder for givet. Anvend system metoder hvor muligt, og indfør kun udvidelsesmetoder under nøje overvejelser.

Se denne MSDN artikel, som mit eksempel herover er inspireret udfra, for flere uddybende eksempler.