„Computer Vision“ ist eines der wichtigsten, aktuellen Themen in der IT. Überall in modernen Systemen kommt diese Technologie zum Einsatz – sei es in den genialen Autos von Tesla („Object Detection“ für Hindernisse, andere Verkehrsteilnehmer, Straßenschilder, etc), Home Automation („Motion Detection“) oder auch Überwachungs -und Fahndungssystemen („Face Detection“).
KTM bietet bereits eine Fülle von Möglichkeiten, um Informationen aus strukurierten oder unstrukturierten Dokumenten auszulesen. Doch was, wenn man auf einmal Objekte auf Fotos (z.B. Bilder von Häusern aus Exposés) oder sogar Gesichter erkennen muss?
Dieser Blogeintrag soll die Grundidee der Erweiterbarkeit von Kofax Transformation Modules anhand von Gesichtserkennung demonstrieren.
1. Das richtige Framework – eine wichtige Entscheidung
Eines der beliebtesten und auch leistungsstärksten Frameworks für Computer-Vision-Technologien ist OpenCV. Dieses wurde in C/C++ implementiert und kann deshalb nicht direkt aus dem Kofax-Umfeld heraus aufgerufen werden. Da KTM immer noch auf die traditionelle Win-Basic-Sprache setzt (Schenkt uns endlich C#!!!!!), benötigen wir einen Wrapper.
Ich habe mich hier für Emgu CV entschieden: http://www.emgu.com/wiki/index.php/Main_Page
Die aktuellste Version des Wrappers findet ihr hier: https://sourceforge.net/projects/emgucv/files/latest/download?source=files
2. Das Coding
Auch wenn man auf dem Gebiet der Computer Vision noch nicht erfahren ist, kann man schon durch die Emgu-Sample-Projekte tolle Ergebnisse erzielen. Ein „Face Detection“-Beispiel findet ihr hier: https://github.com/emgucv/emgucv/tree/master/Emgu.CV.Example/FaceDetection
Wir setzen nun ein einfaches Visual-Studio-Projekt auf (Visual C#, Klassenbibliothek) und schreiben uns eine Methode, die wir wiederum direkt aus KTM heraus aufrufen können.
Die Parameter:
- NET 3.5 (limitiert durch den Emgu Wrapper)
- x86 (Kofax Transformation Modules ist noch keine reine 64-Bit-Anwendung)
- Visual C# Klassenbibliothek
- COM-Fähigkeit
- Einzubindene Verweise: Emgu.CV.UI.dll, Emgu.CV.UI.GL.dll, Emgu.CV.World.dll und System.Drawing
using Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Runtime.InteropServices;
namespace FaceDetection
{
[ComVisible(true)]
[Guid("7ac97316-8975-48fb-9af7-d137139c011e")]
[ProgId("FaceDetection.FDetect")]
[ClassInterface(ClassInterfaceType.None)]
public class FD : _FDetect
{
public FD()
{
}
public string recognizeFace(string refImage)
{
IImage image;
image = new UMat(refImage, ImreadModes.Color);
long detectionTime;
List faces = new List();
List eyes = new List();
DetectFace.Detect(
image, "haarcascade_frontalface_default.xml", "haarcascade_eye.xml",
faces, eyes,
out detectionTime);
foreach (Rectangle face in faces)
CvInvoke.Rectangle(image, face, new Bgr(Color.Red).MCvScalar, 2);
foreach (Rectangle eye in eyes)
CvInvoke.Rectangle(image, eye, new Bgr(Color.Blue).MCvScalar, 2);
string path = System.IO.Path.GetTempFileName();
image.Save(path);
return path;
}
}
public interface _FDetect
{
string recognizeFace(string refImage);
}
} |
using Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Runtime.InteropServices; namespace FaceDetection
{
[ComVisible(true)]
[Guid("7ac97316-8975-48fb-9af7-d137139c011e")]
[ProgId("FaceDetection.FDetect")]
[ClassInterface(ClassInterfaceType.None)]
public class FD : _FDetect
{
public FD()
{ } public string recognizeFace(string refImage)
{
IImage image; image = new UMat(refImage, ImreadModes.Color); long detectionTime;
List faces = new List();
List eyes = new List(); DetectFace.Detect(
image, "haarcascade_frontalface_default.xml", "haarcascade_eye.xml",
faces, eyes,
out detectionTime); foreach (Rectangle face in faces)
CvInvoke.Rectangle(image, face, new Bgr(Color.Red).MCvScalar, 2);
foreach (Rectangle eye in eyes)
CvInvoke.Rectangle(image, eye, new Bgr(Color.Blue).MCvScalar, 2); string path = System.IO.Path.GetTempFileName();
image.Save(path);
return path;
}
}
public interface _FDetect
{
string recognizeFace(string refImage);
}
}
Die Methode recognizeFace nimmt den Pfad zu dem ursprünglichen Bild entgegen, versucht Gesicht und Augen zu erkennen und markiert diese dann mit unterschiedlichen Rahmen. Anschließend wird das Bild temporär gespeichert und der Pfad auf das manipulierte Bild zurückgegeben.
3. Integration in KTM/KTD
Als nächstes setzen wir ein einfaches KTM-Projekt auf. Uns reichen eine einfache Dokumentklasse und ein Scriptlokator.

Der Lokator soll nun folgendes machen: Er ruft unsere Bilderkennung auf und tauscht danach das Bild im xDoc aus, so dass wir als Ergebnis das markierte Gesicht sehen:
Private Sub SL_FaceDetection_LocateAlternatives(ByVal pXDoc As CASCADELib.CscXDocument, ByVal pLocator As CASCADELib.CscXDocField)
Dim FSO As Object
Dim sCurrentImage As String, oImage As CscImage
Dim lField As Long
Dim oFaceDetection As FaceDetection.FD
Dim image As String
Set oFaceDetection = New FaceDetection.FD
Set FSO = CreateObject("Scripting.FileSystemObject")
sCurrentImage = pXDoc.CDoc.Pages(0).GetImage.FileName
image = oFaceDetection.recognizeFace(sCurrentImage)
FSO.CopyFile image, sCurrentImage, True
Set oImage = New CscImage
oImage.Load sCurrentImage
pXDoc.CDoc.Pages(0).SetImage oImage
End Sub |
Private Sub SL_FaceDetection_LocateAlternatives(ByVal pXDoc As CASCADELib.CscXDocument, ByVal pLocator As CASCADELib.CscXDocField)
Dim FSO As Object
Dim sCurrentImage As String, oImage As CscImage
Dim lField As Long
Dim oFaceDetection As FaceDetection.FD
Dim image As String Set oFaceDetection = New FaceDetection.FD
Set FSO = CreateObject("Scripting.FileSystemObject") sCurrentImage = pXDoc.CDoc.Pages(0).GetImage.FileName
image = oFaceDetection.recognizeFace(sCurrentImage)
FSO.CopyFile image, sCurrentImage, True
Set oImage = New CscImage
oImage.Load sCurrentImage
pXDoc.CDoc.Pages(0).SetImage oImage
End Sub
Das Ergebnis kann sich sehen lassen 🙂
