-
TECHNISCHES GEBIET
-
Die vorliegende Erfindung betrifft allgemein Signalflussarchitekturen für Anwendungen zur Verarbeitung von Bildern und anderen Daten und, in manchen Ausführungsformen, Werkzeuge zum Generieren von Programmcode, der Signalflüsse implementiert, die auf deren grafischen Darstellungen basieren.
-
HINTERGRUND
-
Anwendungen zur Bildverarbeitung umfassen gewöhnlich mehrere funktionelle Verarbeitungsblöcke, nachstehend als „Knoten” bezeichnet, die sequentiell ausgeführt werden, um Rohbilddaten in die endgültigen Bilder umzuwandeln, die dem Benutzer vorgelegt werden, und/oder um die Bilddaten zu analysieren, um Informationen über von ihnen erfasste Bedingungen oder Gegenstände zu extrahieren. In solchen Anwendungen bildet der Algorithmus, der den erforderlichen Signalfluss steuert, der die Knoten verbindet (d. h. die Eingabe, Ausgabe, vorläufige Datenspeicherung und Datenübertragung für die verschiedenen Funktionsblöcke verwaltet), gewöhnlich den Kern der Anwendung und verbraucht oft einen maßgeblichen Teil der Verarbeitungsleistung – insbesondere, wenn er auf einem digitalen Signalprozessor (DSP) oder in Hardware implementiert ist. 1 zeigt einen beispielhaften Signalfluss eines Algorithmus für Vordergrund-„Blob”-Erkennung, der beispielsweise dazu verwendet werden kann, Personen, Fahrzeuge oder andere Gegenstände auf Bildern festzustellen. Der erste Knoten 100 (,ABS DIFF') berechnet die auf Pixel bezogene Differenz der Bildwerte (z. B. Schwarzweiß-Werte) zwischen einem Bild und einem Hintergrundbezugsbild. Im zweiten Knoten 102 (,BINÄRE SCHWELLE') wird der so berechnete Unterschied mit einer festen oder adaptiven Schwelle verglichen, um ein binarisiertes Bild zu erzeugen. Das binarisierte Bild wird in den Knoten ,EROSION' und ,DEHNUNG' 104, 106 weiterer Nachbearbeitung unterzogen, um Störpixel zu entfernen und die binäre Bildausgabe zu verbessern. Der letzte Knoten 108 (,VERBUNDEN KENNZ.') stellt verbundene Pixel im binären Bild fest und kennzeichnet sie als „Blobs”.
-
Das Entwickeln eines geeigneten Programmcodes zum Implementieren des Signals und Datenflusses (ob in einer Low-Level-DSP-Sprache oder einer High-Level-Sprache wie C oder C++ geschrieben) ist gewöhnlich eine mühsame Aufgabe für den Programmierer des Algorithmus oder der Anwendung, und umfasst viele Ebenen der Designoptimierung in Bezug auf Speicherzuweisung, direkten Speicherzugriff, Steuerung usw. Daher ist es wünschenswert, diese Aufgabe ganz oder teilweise zu automatisieren. Es stehen Programmierwerkzeuge zur Verfügung, die Code von einer diagrammatischen Darstellung eines Signalflusses, der vom Anwendungsentwickler in einer grafischen Benutzerschnittstelle (GUI) erstellt wurde, automatisch generieren. Diese Werkzeuge unterstützen gewöhnlich Signalflussarchitekturen, die entweder auf Abtastungen oder auf Frames basieren, wobei die Verarbeitungsknoten individuelle Datenabtastungen bzw. ganze Frames bearbeiten. Auf Abtastungen basierende Werkzeuge sind beispielsweise in der Audiosignalverarbeitung und Motorsteuerung weit verbreitet. Sie können für viele Bildverarbeitungsanwendungen jedoch ungeeignet sein, die gewöhnlich höhere Abtastverarbeitungsraten erfordern, beispielsweise weil ein einzelnes Bild bereits eine große Anzahl Datenabtastungen (d. h. Pixel) enthält, und die ferner häufig Verarbeitungsschritte umfassen, die eine Sammlung von Abtastungen (anstatt einzelne Abtastungen) bearbeiten. Beispielsweise kann ein Bildglättungsschritt das Ersetzen jedes Pixels mit einem Durchschnitt aus einem Block mehrerer Pixel beinhalten, und eine eindimensionale Fourier-Transformation erfordert an sich für jedes Pixel des Ausgabebilds eine vollständige Zeile des Eingabebilds. Andere Werkzeuge bearbeiten ganze Einzelbilder. Das Verarbeiten ganzer Einzelbilder ist jedoch unter vielen Umständen nicht notwendig. Ferner erfordern auf Frames basierende Architekturen in tatsächlichen Bildverarbeitungsanwendungen, die auf DSPs oder anderen Spezialprozessoren mit begrenztem örtlichem Speicher (anstelle eines Computers für allgemeine Zwecke) implementiert sind, häufigen Zugriff auf externen Speicher (außerhalb des Chips), der das System ineffizient macht.
-
Dementsprechend besteht ein Bedarf an Signalflussarchitekturen, die die effiziente Bildverarbeitung auf DSPs und anderer Hardware ermöglichen, die Speicher- und Bandbreiteneinschränkungen unterliegen, sowie an Werkzeugen, die Anwendungsentwickler beim Implementieren solcher Signalflüsse helfen.
-
KURZDARSTELLUNG
-
Die vorliegende Erfindung betrifft Signalflussarchitekturen, die die Verarbeitung blockbasierter Daten (insbesondere Bilder) erleichtern und viele der Mängel von Signalflüssen auf Abtast- oder Framebasis beheben. Blockbasierte Signalverarbeitung dient gewöhnlich dazu, die Speicheranforderungen im Zusammenhang mit einzelnen Verarbeitungsschritten und die Häufigkeit von Zugriffen auf externe Speicher zu reduzieren und damit die Gesamteffizienz im Vergleich zur Verarbeitung auf Framebasis zu erhöhen. Ein Daten-„Block” ist im vorliegenden Dokument eine Sammlung von Datenabtastungen, die eine Untergruppe eines vollständigeren Datensatzes bilden, der als „Frame” oder „Einzelbild” bezeichnet wird. Beispielsweise enthält ein Einzelbild die Gesamtheit der visuellen Datenabtastungen, die von einer Kamera oder einem anderen optischen Detektor zu einem bestimmtem Zeitpunkt aufgenommen wurden, und bildet gewöhnlich eine zweidimensionale Matrix oder ein Feld von Bildpixeln (obwohl ein- oder dreidimensionale visuelle Datensätze ebenfalls im Bedeutungsbereich des Begriffs „Einzelbild” liegen). Ein Bildblock kann beispielsweise aus einer oder mehreren Zeilen oder Spalten des Einzelbilds oder aus einem Unterfeld bestehen, das Teile von mehreren Zeilen und/oder Spalten umfasst, die ein bestimmtes Pixel umgeben. Obwohl die Bildverarbeitung eine der Hauptanwendungen blockbasierter Signalverarbeitung ist und in dieser Patentschrift zu Anschauungszwecken ausgiebig verwendet wird, versteht es sich, dass die Erfindung nicht auf Bilddaten beschränkt ist, sondern allgemein auf beliebige Datentypen zutrifft, für die blockbasierte Verarbeitung in Frage kommt (einschließlich beispielsweise Messungen oder Simulationen diskretisierter physikalischer Felder oder Mehrkanal-Audiosignale).
-
Signalflüsse in Übereinstimmung hiermit umfassen gewöhnlich mehrere Knoten, wobei jeder Knoten einer funktionellen Einheit entspricht, d. h. der Leistung einer bestimmten Verarbeitungsfunktion am Eingabedatenblock. Die Knoten können allgemein in Hardware (d. h. Schaltkreise), Software (d. h. ausführbare Anweisungssätze) oder einer Kombination beider implementiert sein. In Softwareimplementierung entspricht in manchen Ausführungsformen jeder Knoten einem getrennten Funktionsblock oder Anweisungssatz, der von einem Prozessor ausgeführt wird; in manchen Ausführungsformen werden ein oder mehrere Knoten jeweils von mehreren Anweisungsblöcken implementiert; und in manchen Ausführungsformen werden zwei oder mehrere Knoten zusammen von nur einem Anweisungsblock implementiert. Ähnlich kann in Hardwareimplementierungen jeder Knoten einem einzelnen zweckbestimmten Schaltkreis entsprechen; mehrere Knoten können von einem Schaltkreis mit mehreren Funktionen implementieren werden; und/oder mehrere getrennte Schaltkreise können gemeinsam einen einzelnen Knoten implementieren. Abhängig vom Kontext kann sich der Begriff „Knoten” nachfolgend auf den Verarbeitungsschritt oder die Verarbeitungsfunktion selbst oder auf dessen/deren Implementierung in Hardware und/oder Software beziehen.
-
In blockbasierten Signalflüssen kann die Blockgröße, die am Knoten zum Erzeugen einer Ausgabeeinheit erforderlich ist (wobei eine Einheit eine einzelne Abtastung oder ein Datenblock sein kann), von Knoten zu Knoten unterschiedlich sein. Beispielsweise kann in Bildverarbeitungsanwendungen ein Bildverarbeitungsschritt eine Eingabezeile bearbeiten, um eine Ausgabezeile zu erzeugen, während ein anderer Schritt drei Eingabezeilen pro Ausgabezeile erfordern kann. Den Knoten sind gewöhnlich Eingabepuffer zum Speichern der notwendigen Datenmenge zugeordnet. In manchen Ausführungsformen wird jeder Knoten ausgelöst, sobald in seinem Eingabepuffer genügend Daten zur Verfügung stehen, um eine Ausgabeeinheit zu erzeugen; auf diese Weise werden die Gesamtanforderungen an den örtlichen Speicher sowie Wartezeiten minimiert. In Ausführungsformen, in denen ein einziger Prozessor oder Schaltkreis mehrere Knoten ausführt, wird jeder Knoten, in dessen Eingabepuffer genügend Daten vorhanden sind, zur Ausführung aktiviert und ausgeführt sobald die Rechnerkapazität des Prozessors oder Schaltkreises dies zulässt. Ein Puffer kennzeichnet im vorliegenden Dokument beliebige Hilfsmittel zur Datenspeicherung und kann in einer beliebigen Art von Speichermedium, -gerät oder -struktur implementiert werden, einschließlich beispielsweise einem oder mehreren Teilbereichen oder Abschnitten in einem RAM-Speicher, Hardwareregistern, Flip-Flops, Kippstufen oder beliebigen Kombinationen davon. Der Puffer muss keine einzige zusammenhängende Einheit bilden, sondern kann mehrere Teile in unterschiedlichen Speicherorten umfassen. Ferner kann er Daten direkt oder indirekt über Zeiger an getrennten Speicherorten speichern.
-
Zusätzlich zu blockbasierten Datenverarbeitungsverfahren und Hardware zu deren Implementierung stellt die vorliegende Erfindung in verschiedenen Ausführungsformen auf grafischen Benutzerschnittstellen (GUIs) basierende Werkzeuge bereit, mit denen Anwendungsentwickler Signalflüsse grafisch vorgeben und geeigneten Programmcode aufgrund der grafischen Signalflussdarstellung automatisch generieren können. Solche Werkzeuge umfassen gewöhnlich eine Bibliothek funktioneller Datenverarbeitungsblöcke (d. h. Knoten), einen Editor zum Zeichnen eines Signalflusses, der Knoten aus der Bibliothek umfasst, einen Compiler zum Generieren von Code aus dem Signalfluss und wahlweise einen Simulator zum Ausführen und Testen des Codes.
-
In einem ersten Aspekt stellt die Erfindung ein Verfahren zur Verarbeitung von Datenframes (wie z. B. Einzelbildern) mittels einer Reihe von Verarbeitungsknoten bereit, wobei jeder Knoten so konfiguriert ist, dass er einen Block von Eingabedaten erzeugt – wobei jeder Block mehrere Abtastungen beinhaltet und einen Teil eines Frames darstellt (z. B. eine Anzahl von Zeilen eines Einzelbilds) – um eine Ausgabeeinheit zu erzeugen (z. B. eine Zeile des Einzelbilds). Das Verfahren umfasst das Empfangen von Daten an Eingabepuffern, die den Knoten zugeordnet sind, und das Veranlassen der Ausführung jedes Knotens, wenn der ihm zugeordnete Eingabepuffer genügend Daten speichert, um eine Ausgabeeinheit zu erzeugen. Das Ausführen eines Knotens kann direkt dadurch veranlasst werden, dass er ausgelöst wird, sobald der Eingabepuffer über genügend Daten verfügt; dies kann beispielsweise bei Ausführungsformen der Fall sein, in denen der Knoten über einen zweckbestimmten Schaltkreis, Prozessor oder eine andere Rechnereinheit verfügt, die lediglich auf das Auslösesignal wartet, bevor er/sie die Verarbeitung beginnt. Alternativ kann die Ausführung eines Knotens indirekt veranlasst werden, indem der Zustand des Knotens so verändert wird, dass die Verarbeitung aktiviert oder zugelassen wird. In diesem Fall wird der Knoten verarbeitet sobald der Prozessor, der die Reihe von Knoten implementiert, freie Kapazitäten hat.
-
In einem zweiten Aspekt stellt die Erfindung ein Verfahren zum Steuern eines Signalflusses in einem datenverarbeitenden System bereit, das eine Reihe von Verarbeitungsknoten implementiert, wobei jeder Knoten so konfiguriert ist, dass er eine Einheit von Ausgabedaten (z. B. eine Datenzeile) aus einem Eingabedatenblock erzeugt, die eine knotenspezifische Integermultiple einer Eingabedateneinheit beinhaltet. Das Verfahren umfasst das Steuern des Signalflusses durch die Reihe von Knoten, indem an den den Knoten zugeordneten Eingabepuffern Daten empfangen und die Ausführung jedes Knotens veranlasst wird (d. h. der Knoten ausgelöst oder aktiviert wird), wenn der ihm zugeordnete Eingabepuffer die entsprechenden knotenspezifischen Intergermultiplen einer Dateneinheit speichert.
-
An jeden Verarbeitungsknoten können Daten von einem vorgelagerten Knoten und/oder einem DMA-Quellknoten empfangen werden. In manchen Ausführungsformen liest der erste Verarbeitungsknoten in der Reihe Daten aus einem DMA-Quellknoten und der letzte Verarbeitungsknoten in der Reihe schreibt Daten in einen DMA-Senkeknoten ein. In bestimmten Ausführungsformen wird für jeden Eingabepuffer ein Zähler verwendet; das Verfahren umfasst dann das Inkrementieren des Zählers für jede Eingabedateneinheit, die von einem vorgelagerten Verarbeitungsknoten oder DMA-Quellknoten empfangen wird. In manchen Ausführungsformen wird Speicher, der einem Puffer zugewiesen ist, der einem der Verarbeitungsknoten zugeordnet ist, für einem Puffer wiederverwendet, der einem diesem nachgelagerten Knoten zugeordnet ist. Die Verarbeitungsknoten können parallel oder sequentiell ausgeführt werden.
-
In einem dritten Aspekt stellt die Erfindung ein System zum Verarbeitung von Datenframes mittels einer Reihe von Verarbeitungsknoten bereit. Jeder Knoten ist so konfiguriert, dass er einen knotenspezifischen Eingabedatenblock verarbeitet, um eine Einheit von Ausgabedaten zu erzeugen, und jeder Block beinhaltet mehrere Datenabtastungen und stellt einen Teil des Datenframes dar. Das System beinhaltet einen oder mehrere Verarbeitungsblöcke, die die Reihe von Verarbeitungsknoten implementieren, mehrere Eingabepuffer, die den Knoten zugeordnet sind, und einen Logikschaltmechanismus, der die Ausführung jedes Knotens durch den jeweiligen Verarbeitungsblock veranlasst, wenn der diesem zugeordnete Eingabepuffer den knotenspezifischen Eingabedatenblock speichert. In manchen Ausführungsformen umfasst das System mehrere Verarbeitungsblöcke, von denen jeder einem der Verarbeitungsknoten entspricht.
-
Der Verarbeitungsblock (die Verarbeitungsblöcke) können mit durch einen Prozessor ausführbaren Anweisungen implementiert werden, die im Speicher gespeichert sind. Alternativ können der Verarbeitungsblock (die Verarbeitungsblöcke) in Schaltkreisen implementiert werden. In manchen Ausführungsformen wird ein einzelner Schaltkreis bereitgestellt, um die Reihe von Verarbeitungsknoten sequentiell auszuführen, und in manchen Ausführungsformen werden mehrere Schaltkreise bereitgestellt, um die Verarbeitungsknoten parallel auszuführen (deren Ausführung bereits vom Logikschaltmechanismus veranlasst oder aktiviert wurde). Ein „Schaltkreis” kann im vorliegenden Dokument in diesem Kontext ein Prozessorkern, ein eigenständiger Teil eines Kerns, eine arithmetische Logikeinheit oder allgemein jede andere funktionelle Verarbeitungseinheit sein. Der Schaltmechanismus kann mehrere Register umfassen, die für jeden Knoten eine Anzahl von Eingabeeinheiten speichert, die dem knotenspezifischen Block dieses Knotens zugeordnet sind, und einen Zähler für eine Anzahl von Eingabeeinheiten, die gegenwärtig im diesem Knoten zugeordneten Puffer gespeichert sind. Die Register können Hardware-Register sein oder im örtlichen Speicher gespeichert sein, der dem Verarbeitungsblock (den Verarbeitungsblöcken) zugeordnet ist. In manchen Ausführungsformen ist das System ein digitaler Signalprozessor.
-
In einem vierten Aspekt stellt die Erfindung ein System zum Generieren von Programmcode für die blockbasierte Signalverarbeitung aus einer grafischen Darstellung eines Signalflusses bereit, der in einer grafischen Benutzerschnittstelle vorgegeben wird. Das System umfasst eine Prozessor, Speicheranweisungen, die vom Prozessor auszuführen sind, und wahlweise eine Anzeigevorrichtung (z. B. einen Computerbildschirm), um die grafische Benutzerschnittstelle anzuzeigen. Die im Speicher gespeicherten Anweisungen umfassen (i) eine Bibliothek von Funktionen, die signalverarbeitende Knoten implementieren, wobei jeder Knoten so konfiguriert ist, dass er eine Einheit von Ausgabedaten aus einem Eingabedatenblock erzeugt, der eine knotenspezifischen Größe aufweist, (ii) Anweisungen, die einen Editor implementieren, so dass der Benutzer einen Signalfluss, der mehrere Knoten und Verbindungen zwischen diesen umfasst, grafisch vorgeben und jedem dieser Knoten eine der Funktionen aus der Bibliothek zuordnen kann, und (iii) Anweisungen, die einen Compiler zum Generieren des Programmcodes aus dem grafisch vorgegebenen Signalfluss und den zugeordneten Funktionen implementieren, wobei der Code die Ausführung jedes Knotens veranlasst, wenn ein diesem Knoten zugeordneter Puffer einen Eingabedatenblock der jeweiligen knotenspezifischen Größe speichert. Der Editor kann dem Benutzer ferner das grafische Vorgeben des Speicherdirektzugriffs (DMA) des Signalflusses, einschließlich z. B. DMA-Quellen, DMA-Senken und/oder DMA-Planungspfade ermöglichen, und der Compiler kann ferner Programmcode generieren, der den grafisch vorgegebenen DMA implementiert. Gibt der Benutzer keinen Planungspfad vor, kann der Compiler den DMA-Planungspfad automatisch generieren. Der Compiler kann ferner Programmcode generieren, der Datenparallelitäten in DMA-Pfaden löst und Pingpong-Puffer an Quellknotenpuffern und Senkeknotenpuffern sowie Code, der die den Knoten zugeordneten Puffer implementiert, auf verschiedenen Ebenen der Speicherhierarchie zuweist. Der Editor kann dem Benutzer das Eingeben von Parametern in ein DMA-Paramerterfenster ermöglichen, und der Compiler kann aufgrund der Parameter DMA-Registereinträge generieren.
-
KURZE BESCHREIBUNG DER ZEICHNUNGEN
-
Das Voranstehende kann anhand der folgenden ausführlichen Beschreibung der Erfindung, insbesondere im Zusammenhang mit den Zeichnungen, leichter verstanden werden. Es zeigen:
-
1 ein herkömmliches Signalflussdiagramm für eine beispielhafte Bildverarbeitungsanwendung;
-
2A ein konzeptionelles Signalflussdiagramm, das die Pufferanforderungen für jeden Knoten in einer Implementierung von auf Zeilen basierender Signalverarbeitung darstellt;
-
2B ein konzeptionelles Signalflussdiagramm, das die reduzierten Pufferanforderungen für die Knoten in einer auf Zeilen basierender Implementierung der Signalverarbeitung darstellt, die Schalter zwischen den Knoten gemäß einer Ausführungsform der Erfindung einbezieht;
-
3 ein Blockdiagramm, das ein System zur auf Zeilen basierenden Signalverarbeitung gemäß verschiedener Ausführungsformen darstellt;
-
4A und 4B jeweils einen auf Zeilen basierenden Signalverarbeitungsfluss, der Schalter zwischen Knoten und Register einbezieht, die den Datenfluss und das Betätigen der Schalter gemäß verschiedener Ausführungsformen steuern;
-
5 ein Blockdiagramm, das ein auf einer GUI basierendes Werkzeug zum grafischen Bestimmen von Signalflüssen und automatischen Generieren von darauf basierendem Code gemäß einer Ausführungsform darstellt;
-
6A–6H Komponenten einer Benutzerschnittstelle eines auf einer GUI basierenden Werkzeugs gemäß verschiedener Ausführungsformen; und
-
7 ein Blockdiagramm, das ein Computersystem zum Implementieren des auf einer GUI basierenden Werkzeugs von 5 gemäß verschiedener Ausführungsformen darstellt.
-
AUSFÜHRLICHE BESCHREIBUNG
-
Datenverarbeitungsalgorithmen gemäß verschiedener Ausführungsformen bearbeiten Datenblöcke anstelle Datenabtastungen oder vollständige Frames. Solche Blöcke können beispielsweise aus aus einer oder mehreren Zeilen eines zweidimensionalen Datenfelds oder aus einem oder mehreren Slices eines dreidimensionalen Felds bestehen. Zeilenbasierte Datenverarbeitung ist für viele Bildverarbeitungsanwendungen geeignet oder sogar notwendig, z. B., um zweidimensionales Filtern wie Faltung oder zweidimensionales Morphologiefiltern wie Erosion und Dehnung durchzuführen. 2A und 2B stellen Signalflüsse für einen beispielhaften zeilenbasierten Algorithmus dar, der vier Knoten 200, 202, 204, 206 oder Verarbeitungsschritte, umfasst, wobei jeder eine gewisse Anzahl von Eingabezeilen (links vom Knoten angezeigt) in eine Ausgabedatenzeile umwandelt. Für aufeinanderfolgende Ausgabezeilen überlappen die entsprechenden Blöcke aus mehreren Eingabezeilen so, dass jeder Frame von Eingabedaten einen Frame gleicher Größe von Ausgabedaten ergibt. Beispielsweise kann Zeile n des Ausgabeframes aus den Zeilen n – 1, n und n + 1 des Eingabeframes generiert werden, so dass die dreizeiligen Eingabeblöcke zwei nebeneinanderliegender Ausgabezeilen um zwei Zeilen überlappen. (Der Eingabeframe kann „gefüllt” werden, z. B. mit Nullen, um die um die Ränder des Frames benötigten Eingabedaten bereitzustellen. Alternativ, falls der Eingabeframe selbst nicht gefüllt ist, generieren die Knoten die gefüllten Nullen, um die Puffer aufzufüllen.)
-
Im dargestellten Signalfluss empfängt der erste Knoten 200, „Knoten 0” Eingabedaten über direkten Speicherzugriff (DMA) von einem DMA-Quellknoten 208 und der letzte Knoten 206 „Knoten 3” schreibt über direkten Speicherzugriff Ausgabedaten in einen DMA-Senkeknoten 210 ein. Jedem Knoten ist an seinem Eingang ein Puffer zugeordnet, der die Ausgabe vom direkt vorgelagerten Knoten vorübergehend speichert. In einer Ausführungsform, die in 2A dargestellt ist, sind die Puffer größenmäßig so ausgelegt, dass sie genügend Daten zum Generieren einer Ausgabezeile an Knoten 3 speichern können. Knoten 3 benötigt eine Eingabezeile, um eine Ausgabendatenzeile zu erzeugen; entsprechend ist sein Puffer 212 so konfiguriert, dass er eine Datenzeile speichern kann. Knoten 2 benötigt fünf Eingabezeilen für eine Ausgabezeile und erfordert somit einen Puffer 214, der fünf Datenzeilen speichert. Knoten 1 benötigt drei Eingabezeilen, um eine Ausgabezeile zu erzeugen. Um die fünf Eingabezeilen zu erzeugen, die von Knoten 2 benötigt werden, benötigt Knoten 1 jedoch insgesamt sieben Eingabedatenzeilen: Zeilen 1–3 ergeben die erste Ausgabezeile, Zeilen 2–4 die zweite Ausgabezeile, Zeile 3–5 die dritte Ausgabezeile, Zeilen 4–6 die vierte Ausgabezeile und Zeile 5–7 die fünfte Ausgabezeile. Folglich ist der Eingabepuffer 216 an Knoten 1 so konfiguriert, dass sieben Zeilen gespeichert werden können. Ähnlich benötigt Knoten 0 fünf Eingabezeilen um eine Ausgabezeile zu generieren, um jedoch die sieben Datenzeilen bereitzustellen, die von Zeile 1 benötigt werden, erfordert er einen Puffer 218, der insgesamt elf Eingabezeilen speichert (Zeilen 1–5 für die erste Ausgabezeile, Zeilen 2–6 für die zweite Ausgabezeile usw.). Allgemein benötigt ein Knoten, der eine Ausgabezeile aus n Eingabezeilen erzeugt, und der einem Knoten vorgelagert ist, der insgesamt m Eingabezeilen erfordert, einen Puffer, der n + m – 1 Zeilen speichert. Daher kaskadiert die erforderliche Anzahl von Eingabezeilen bis zum Quellknoten 208. Für Signalflüsse, die eine große Anzahl von Knoten beinhalten, können die sich daraus ergebenden Pufferanforderungen die Kapazität des örtlichen Speichers überschreiten und Zugriffe auf externe Speicher notwendig machen, was durch die blockbasierte Verarbeitung ausgeschlossen werden sollten.
-
2B zeigt einen modifizierten Signalfluss, der das Speicherproblem vermindert. Hier wird der Datenfluss durch einen Steuersignalfluss gesteuert, der jeden Knoten auslöst, sobald die Datenmenge im Eingabepuffer des Knotens groß genug ist, um eine Ausgabezeile zu erzeugen – konzeptionell wird dies durch die Schalter 220 dargestellt, die jeweils die Ausgabe eines Vorgängerknotens mit der Eingabe des nächsten Knotens verbinden, und die sich schließen, wenn im Eingabepuffer des nächsten Knotens genügend Daten vorhanden sind. Wenn daher beispielsweise Knoten 1 drei Eingabeknoten empfangen hat, verarbeitet er diese Zeilen, um eine Ausgabezeile zu generieren, die er im Eingabepuffer von Knoten 2 speichert. Der Eingabepuffer von Knoten 1 kann dann überschrieben werden. Insbesondere können die zweite und dritte Zeile im Eingabepuffer um eine Zeile nach oben verschoben werden (wobei die erste Zeile überschrieben wird), und die nächste Eingabezeile kann (von Knoten 0) empfangen und in der dritten Zeile des Eingabepuffers von Knoten 1 gespeichert werden. Auf diese Weise wird die erforderliche Puffergröße an jedem Knoten auf die Anzahl der Eingabezeilen reduziert, die der Knoten benötigt, um eine Ausgabezeile zu erzeugen; im Beispiel von 2B hätten die Knoten 0, 1, 2 und 3 die Puffer 222, 224, 226, 228 für fünf Zeilen, drei Zeilen, fünf Zeilen bzw. eine Zeile. Für den Durchschnittsfachmann ist leicht ersichtlich, dass die kaskadierende Wirkung, die den Signalfluss von 2A beeinträchtigt, auf diese Weise eliminiert, und die Pufferanforderungen insgesamt sind ungefähr proportional zu der Anzahl der Knoten oder geringer, wenn Puffer von Knoten gemeinsam genutzt werden können (wie unten erklärt). Die Puffer für die einzelnen Knoten können, anhängig von der Puffergröße und der erforderlichen Zugriffshäufigkeit, gewöhnlich auf verschiedenen Ebenen der Speicherhierarchie implementiert werden. Beispielsweise kann der kleinste Puffer (für einzelne Datenzeilen) im L1-Speicher implementiert werden, wohingegen größere Puffer im L2- oder L3-Speicher oder im Zwischenspeicher (die größere Latenzzeiten aufweisen) implementiert werden können.
-
Der in 2B gezeigte Signalfluss kann unter Verwendung eines einzigen Prozessors ausgeführt werden. In diesem Fall wird der Prozessor so gesteuert, dass er von den Knoten, die an ihren Eingabepuffern über ausreichend Daten verfügen, immer den Knoten ausführt, der am weitesten in Richtung des DMA-Senkeknotens 210 liegt. Beispielsweise beginnend mit einem neuen Datenframe an der Eingabe, wird Knoten 0 dreimal ausgeführt, um die Mindestdatenmenge zu erzeugen, die von Knoten 1 benötigt wird. Dann schließt sich der Schalter zwischen den Knoten 0 und 1, und Knoten 1 wird ausgeführt, um eine Ausgabezeile zu erzeugen. Danach werden die zweite und die dritte Zeile im Eingabepuffer von Knoten 1 um eine Zeile nach oben verschoben, und Knoten 0 wird erneut ausgeführt, um die dritte Datenzeile für den Eingabepuffer von Knoten 1 zu erzeugen. Als nächstes wird Knoten 1 erneut ausgeführt, um die zweite Datenzeile für den Eingabepuffer von Knoten 2 zu generieren. Dieser Vorgang wird noch dreimal wiederholt, bis an Knoten 2 fünf Eingabezeilen bereitstehen, woraufhin Knoten 2, gefolgt von Knoten 3 ausgeführt wird. Der Prozessor kehrt dann zu Knoten 0 zurück und die gesamte Schleife wird wiederholt, bis der gesamte Eingabedatenenframe verarbeitet wurde. Während jeder Schleife kann Speicher zwischen den Knoten erneut verwendet werden, d. h. Pufferplatz kann gemeinsam genutzt werden. Beispielsweise kann die Ausgabezeile von Knoten 2 in dem Speicher gespeichert werden, der zuvor dem Eingabepuffer von Knoten 1 zugeteilt war. (Sollte dieser Puffer noch immer die drei bereits verarbeiteten Zeilen speichern, kann die erste Zeile von der Ausgabe von Knoten 2 überschrieben werden, da sie nicht länger benötigt wird. Andererseits kann, wenn die vorigen Zeilen 2 und 3 des Eingabepuffers von Knoten 1 bereits nach Zeile 1 und 2 kopiert wurden, die dritte Zeile dieses Puffers dazu verwendet werden, die Ausgabe von Knoten 2 zu speichern.)
-
In manchen Ausführungsformen werden die verschiedenen Knoten des Signalflusses von mehreren Prozessoren parallel oder von nur einem Prozessor ausgeführt, der mehrere Threads gleichzeitig betreibt (z. B. auf zeitgestaffelte, überlappende Weise). In diesem Fall ist eine erneute Verwendung von Speicher unter den Puffer nicht möglich, jedoch kann die Gesamtausführungszeit drastisch reduziert werden, da jeder Knoten wiederholt solange ausgeführt wird, wie der Schalten an seiner Eingabe geschlossen ist, d. h. solange in seinem Eingabepuffer ausreichend Daten vorhanden sind. Üblicherweise wird, sobald der Puffer zwischen zwei Knoten gefüllt ist (z. B. der Eingabepuffer von Knoten 1 drei Eingabezeilen empfangen hat) und der Schalte zwischen den Knoten geschlossen wurde, der Schalter von einem Ende gefüllt und vom anderen Ende mit der gleichen Geschwindigkeit geleert, und der Schalter bleibt geschlossen, bis der gesamte Frame verarbeitet ist. Mit anderen Worten erfolgt die Datenbewegung durch die Knoten nach dem anfänglichen Füllen der Puffer mittels Pipelining.
-
Der in 2B gezeigte Signalfluss kann auf verschiedene Weisen modifiziert werden. Zusätzlich zum Einbeziehen von mehr (oder weniger) Verarbeitungsknoten mit Anforderungen an die Dateneingabe, die sich von den gezeigten unterscheiden, kann der Signalfluss zusätzliche DMA-Quellknoten und DMA-Senkeknoten umfassen und/oder mit diesen verbunden sein. Im Allgemeinen kann jeder Knoten im Signalfluss Daten aus dem Speicher einlesen (d. h. ein Quellknoten sein) oder Daten an den Speicher herausschreiben (d. h. ein Senkeknoten sein). Ferner müssen die Knoten im Signalfluss nicht unbedingt eine lineare Kette bilden. In manchen Anwendungen umfasst der Signalfluss zwei oder mehr parallele Knoten, d. h. Knoten, die Eingabedaten unabhängig voneinander verarbeiten und deren gemeinsame Ausgaben von einem anderen, im Signalfluss nachgelagerten, Knoten benötigt werden können. Selbstverständlich kann sich der Signalfluss grundsätzlich auf beliebig komplexe Weisen verzweigen und wieder vereinen. Darüber hinaus können bestimmte Knoten in manchen Ausführungsformen optional sein. Beispielsweise kann in einer typischen Bildverarbeitungsanwendung ein Knoten zum Reduzieren der Auflösung, und folglich der Größe von Einzelbildern, abhängig von einer Einstellung, die beispielsweise vom Benutzer der Anwendung vorgenommen wird, ausgeführt werden oder nicht. Um einen solchen optionalen Knoten zu implementieren, kann der Signalfluss einen Bypass um den Knoten umfassen, z. B. unter Verwendung der „Schalter” an der Eingabe und Ausgabe des Knotens, und zusätzlicher Steuersignalleitungen, die die Schaltereinstellungen aufgrund der Benutzerwahl (oder einer anderen Bedingung, z. B. dem Zahlenwert einer von einem vorhergehenden Verarbeitungsschritt abgeleiteten Metrik) bestimmen.
-
Obwohl die Verwendung von „Schaltern” zum Auslösen des Betriebs von Knoten oben anhand des Beispiels der zeilenbasierten Verarbeitung gezeigt wurde, kann sie, unabhängig davon, welche Form und Größe die Datenblöcke jeweils annehmen, gewöhnlich auf jede Art blockbasierter Verarbeitung angewendet werden. Ausschlaggebend ist, dass die Ausführung jedes Knotens innerhalb des Signalflusses dann ausgelöst wird, wenn in seinem Eingabepuffer eine Datenmenge empfangen wurde, die ausreicht, um eine Ausgabeeinheit zu erzeugen, wobei die Größe der Einheit von der jeweiligen Anwendung abhängt. Man kann beispielsweise einen Bildglättungsschritt (d. h. Knoten) betrachten, der den Wert jedes Pixels durch den durchschnittlichen Wert eines 3 × 3-Pixelblocks im Zentrum des besagten Pixels ersetzt. Die Größe der Ausgabeeinheit dieses Knotens beträgt nur einem Pixel; er wird ausgeführt, wenn er in seinem Eingabepuffer einen 3 × 3-Block aufweist – z. B. einem Block entsprechend, der bei den Koordinaten (n, m) des Einzelbilds zentriert ist – und schreibt den berechneten Ausgabewert in den Eingabepuffer des nächsten Knoten so ein, dass die Koordinaten des Pixels (n, m) erhalten bleiben. Der nächste 3 × 3-Block, der vom Knoten verarbeitet wird, kann um eine Spalte nach rechts verschoben werden (d. h. bei (n, m + 1) im Einzelbild zentriert werden) und die berechnete Ausgabe kann dementsprechend im Eingabepuffer der direkt folgenden Knoten mit Zuordnung zu den Koordinaten (n, m + 1) gespeichert werden.
-
Die Größe des Eingabedatenblocks für jeden Knoten ist gewöhnlich ein ganzzahliges Vielfaches der Größe der Ausgabeeinheit vom direkt vorgelagerten Knoten (oder, wenn ein Knoten Eingaben aus einer Gruppe vorgelagerter Knoten aufnimmt, die kombinierte Ausgabeeinheitsgröße der vorgelagerten Knotengruppe), so dass die wiederholte Ausführung der (des) vorgelagerten Knoten(s) die erforderliche Datenmenge für den Eingabeblock generieren kann. In verschiedenen Ausführungsformen ist die Größe der Ausgabeeinheit für alle Knoten gleich. Beispielsweise ist die Ausgabeeinheit jedes Knotens im Signalfluss von 2B eine Datenzeile, und die Eingabedatenblöcke, die von den Knoten benötigt werden, bestehen alle aus ganzzahligen Anzahlen von Zeilen. Die Verwendung einer einzigen Ausgabeeinheit für alle Knoten kann das Programmieren des Signalflusses und auch die DMA-Bewegung der Daten im Signalfluss vereinfachen, ohne das Speichern von Zustandsvariablen für jeden Knoten notwendig zu machen, wie sie für andere blockbasierte Verarbeitungsansätze, wie 8 × 8-Überlappungsblöcke über Spaltengrenzen hinweg erforderlich sind. (Bei der Verarbeitung von 8 × 8-Überlappungsblöcken (im Gegensatz zu zeilenbasierter Verarbeitung, bei der der Knoten mit einem Puffer der Größe 8 × M arbeitet, wobei M die Anzahl der Pixel in einer Zeile des Bilds ist) erfordert die Überlappung für die nächsten zu verarbeitenden 8 × 8-Datenblock, dass die vorherige Spalte vom vorherigen 8 × 8-Block in diesem Knoten als eine Zustandsvariable gespeichert wurde.)
-
3 stellt eine beispielhafte DSP-Implementierung von blockbasierten Signalverarbeitungsflüssen wie dem in 2B gezeigten dar. Der DSP 300 implementiert jeden Knoten des Signalflusses mit getrennter Hardware („Verarbeitungsblock” genannt), wie einer zweckbestimmten Logikeinheit oder einem zweckbestimmten Prozessorkern; daher können die Verarbeitungsblöcke in der dargestellten Ausführungsform Daten parallel mittels Pipelining verarbeiten. Um spezifisch zu sein, zeigt die dargestellte Ausführungsform nur zwei Verarbeitungsblöcke 302, 304. Es versteht sich jedoch, dass Signalflüsse mit einer beliebigen Anzahl Knoten implementiert werden können. Die Daten bewegen sich zwischen den Verarbeitungsblöcken 302, 304 und werden vorübergehend in Eingabe-/Ausgabepuffern 306, 308, 310 gespeichert. Eine Registerbank 312 speichert Steuerparameter, die den Füllzustand der Puffer 306, 308, 310 überwachen, und lösen Datenfluss zwischen den Puffern und Verarbeitungsblöcken 302, 304 aus – mit anderen Worten steuert die Registerbank 312 die „Schalter” 220 zwischen den Knoten. Der Eingabepuffer 306 des ersten Verarbeitungsblocks empfängt Daten von einer internen oder externen Datenquelle 314, 316 wie einem Speicher, einer Kamera, die Bildströme bereitstellt, oder einer anderen Eingabevorrichtung; ein Multiplexer 318 kann die Wahl zwischen mehreren solcher Datenquellen erleichtern. Der Ausgabepuffer 310 des letzten Verarbeitungsknoten 304 sendet Daten (wahlweise über einen anderen Multiplexer 320) an eine interne oder externe Datensenke 322, wie z. B. einen Speicher oder eine Anzeigevorrichtung.
-
In Video- oder Bildverarbeitungsanwendungen, die auf einem DSP oder Hardware implementiert werden (anstatt in Software auf einem Computer für allgemeine Zwecke ausgeführt zu werden), sind die Einzelbilder gewöhnlich zu groß, um örtlich gespeichert zu werden, und befinden sich daher in einem langsameren externen Speicher (in 3 der Datenquelle 316 entsprechend). Bilddaten werden in den internen Speicher in Zeilen oder Blöcken geladen. Nachdem diese Zeilen oder Datenblöcken in einer Reihe von Verarbeitungsblöcken (z. B. Blöcken 302, 304) verarbeitet wurden, wird die generierte Ausgabe ebenfalls im langsameren externen Speicher gespeichert (entsprechend der Datensenke 322, das der gleiche Speicher wie Quelle 316 sein kann). Die Bewegung von Daten zum und vom externen Speicher in die internen Datenpuffer (z. B. Puffer 306, 310) erfolgt vorzugsweise über einen DMA-Controller, der ein integraler Bestandteil der meisten DSPs und anderer Spezialprozessoren ist, und kann z. B. in den Multiplexer 318, 320 implementiert werden. Die externe Datenquelle und -senke 316, 322 sind in diesem Fall DMA-aktiviert. Die DMA-Bewegung hat den zusätzlichen Vorteil, dass die Datenbewegung zum und vom Prozessor mit der Datenverarbeitung selbst parallelisiert wird; das heißt, dass die Datenbewegung im Hintergrund stattfindet.
-
Die Hardware-Ausführungsformen nach 3 ist selbstverständlich nur ein Beispiel. Wie es für den Fachmann leicht ersichtlich ist, können Signalflüsse gemäß verschiedenen Ausführungsformen der Erfindung auf mehrere verschiedene Arten implementiert werden. Beispielsweise kann ein DSP einen einzigen Prozessorkern verwenden, um den verschiedenen Knoten entsprechende Anweisungssätze auszuführen, die beispielsweise im örtlichen Anweisungsspeicher gespeichert sind. Ferner können Datenpuffer den gleichen Speicherplatz gemeinsam nutzen und nach Bedarf on-the-fly erstellt und/oder überschrieben werden. Die Register zum Steuern der Schalter zwischen Knoten können Hardware-Register sein oder alternativ im örtlichen Speicher zusammen mit den Puffern und/oder Anweisungen gespeichert sein. Alternativ zu einem DSP kann der Signalfluss auch auf jeder anderen Art von Spezialprozessoren ausgeführt werden, einschließlich z. B. Mikrocontroller, anwendungsspezifischer integrierter Schaltkreise (ASICs), feldprogrammierbarer Gate-Arrays (FPGAs) oder programmierbarer Gate-Array (PGAs). Darüber hinaus können Signalflüsse in Übereinstimmung hiermit in Software implementiert werden, die auf einem Computer für allgemeine Zwecke ausgeführt wird.
-
Zurückkommend auf eine Hardwareausführung (wie z. B. in 3 gezeigt) stellen 4A und 4B den Betrieb der Schalter, wie er von Registern 312 implementiert wird, ausführlicher dar. Im gezeigten Beispiel umfasst der Signalfluss (dargestellt in 4A) zwei Knoten 400, 402. Jeder der Knoten 400, 402 ist an seiner Eingabe und Ausgabe ein- oder zweidimensionalen Datenpuffern zugeordnet. Insbesondere empfängt Knoten 0, wie gezeigt, Eingabe von zwei mit dem Knoten über Schalter, 404, 406 verbundene DMA-Quellknoten S0, S1, die seine Datenpuffer B0, B1 (408, 410) speisen. Die Ausgabe von Knoten 0 speist einen zweidimensionalen Puffer B2 (412), der wiederum über einen Schalter 414 mit der Eingabe von Knoten 1 verbunden ist. Die Ausgabe von Knoten 1 fließt in den Datenpuffer B3 (416), der mit einer DMA-Senke S2 über einen Schalter 418 verbunden ist.
-
Das System unterhält vier verschiedene Registerfelder 420, 422, 424, 426, die zusammen die Registerbank 312 zur Steuerung des Signalflusses durch die Knoten 400, 402 bilden. Jedes Feld kann eine Anzahl von beispielsweise 32-Bit-Registern umfassen. Ein Knotenquellen-Adressregisterfeld 420 umfasst ein Register für jede Eingabequelle eines Knotens: im gezeigten Beispiel zwei Einträge für die beiden Eingabequellen von Knoten 0 und einen Eintrag für die Eingabequelle von Knoten 1. Die Einträge in diese Register sind die Adressen der Datenpuffer für die jeweiligen Eingabequellen, d. h. die Adressen der Puffer B0, B1 und B2. Nachdem sie initialisiert wurden, ändern sich diese Registereinträge während der gesamten Signalverarbeitung nicht. Ein Knotenziel-Adressregisterfeld 422 umfasst einen Registereintrag für jede Ausgabe eines Knotens: beispielsweise ein Register für die Ausgabe von Knoten 0 und einen Eintrag für die Ausgabe von Knoten 1. Die Einträge in diese Register sind die Adressen der Datenpuffer für die jeweiligen Ausgaben, z. B. Puffer B2 und B3. Für eindimensionale Puffer an der Ausgabe eines Knotens ändern sich die Werte in den entsprechenden Registern nicht, nachdem sie initialisiert wurden. Für zweidimensionale Puffer an der Ausgabe (z. B. Puffer B2 an der Ausgabe von Knoten 0) wird der Eintrag auf den Wert B2 + 2. Zeile initialisiert (d. h. die erste Pufferadresse im Register ist die Speicheradresse der 2. Zeile im Puffer B2), und nach der ersten Iteration ändert sich der Eintrag in B2 + 3. Zeile und bleibt danach gleich. (Die Ausgabe des vorherigen Knotens, der für den aktuellen Knoten die Funktion des Eingabepuffers erfüllt, wird nach der ersten Iteration immer in die gleiche Speicheradresse eingeschrieben und muss daher nicht aktualisiert werden. Dies vereinfacht die Datenbewegung zwischen Knoten, die 2D-Puffer aufweisen. Es ist zu beachten, dass die Iteration von der Größe des Puffers abhängt. Ist der Puffer 3 × M so ist die Iteration 3/2 = 1, ist er 5 × M, so ist die Iteration 5/2 = 2 usw.)
-
Ein Schalterwert-Registerfeld 424 bestimmt, wann der Steuerschalter für jeden Knoten geschlossen wird: Er speichert in jedem Register die Mindestanzahl von Zeilen (oder, allgemeiner, Einheiten) von Eingabedaten, die im Eingabepuffer des jeweiligen Knotens erforderlich sind, um den Schalter zu schließen und die Ausführung des Knotens auszulösen. Beispielsweise erfordert der Schalter 404, der Puffer B0 mit Knoten 0 verbindet, dass der Puffer B0 eine Datenzeile speichert; dieser Wert wird im Register gespeichert. Schalter 414 verbindet Puffer B2 mit Knoten 1; die zugeordneten Pufferanforderungen von drei Zeilen ist im entsprechenden Register gespeichert. Schließlich ist im Knotenzähler-Registerfeld 426 jeder Registereintrag einem Steuerschalter für entweder eine Eingabequelle zu einem Knoten oder einer DMA-Senke zugeordnet, mit der ein Knoten verbunden ist. Diese Registerwerte sind Zähler, die die Anzahl der Datenzeilen verfolgen, die vom jeweiligen Knoten verarbeitet wurden (und somit vom Knoten jedes Mal aktualisiert werden, wenn eine Eingabedatenzeile verarbeitet wurde), und steuern, wann sich der Schalter zum Knoten schließt. Zuerst sind die Registerwerte C0, C1 und C3 für die Puffer B0, B1 und B3 alle null und der Registerwert C2 für den Puffer B3 ist eins. (Für zweidimensionale Puffer kann der erste Wert so gewählt werden, dass er die Anzahl der Zeilen im Puffer dividiert durch 2 und abgerundet ist, was im vorliegenden Beispiel zu einem gerundeten Wert von (3/2) = 1 führt.) Wenn dann der DMA-Quellknoten S0 den Puffer B0 mit einer Zeile Pixel (oder Abtastungen) füllt, und der DMA-Quellknoten S1 den Puffer B1 mit einer Zeile Pixel füllt, werden die Werte auf C0 = 1 und C1 = 1 aktualisiert. Der Schalter, der jedem Zählerregister zugeordnet ist, schließt sich, wenn sein Wert gleich oder größer als der Wert wird, der im entsprechenden Schalterregister gespeichert ist. In Pseudosyntax entspricht dies der folgenden „Wenn-Anweisung”:
wenn(C0 >= SCHALTER0 und C1 >= SCHALTER 1)
{schließe Schalter 0 und Schalter 1; verarbeite Knoten 0; erhöhe C2 um 1}
wenn(C2 >= SCHALTER2)
{schließe Schalter 2; verarbeite Knoten 1; erhöhe C3 um 1}
-
In verschiedenen Ausführungsformen stellt die vorliegende Erfindung ein auf einer GUI basierendes Werkzeug bereit, mit dem Anwendungsprogrammierer hierin beschriebene Signalflüsse entwerfen und implementieren können. Wie in 5 konzeptionell dargestellt, umfasst das GUI-Werkzeug 500 einen Editor 502, der einen Zeichenbereich 504 und zugehörige Zeichengeräte 506 aufweist, mit denen der Programmierer eine Abbildung des gewünschten Signalflusses erstellen kann. Ferner umfasst das Werkzeug eine Bibliothek 508 von Funktionen oder Verfahren – d. h. eigenständigen Sätze computerausführbarer Anweisungen, – die verschiedene diskrete bildverarbeitende Algorithmen implementieren und, wenn gewählt, als die funktionellen Knoten, d. h. die Verarbeitungsblöcke, des Signalflusses fungieren. In manchen Ausführungsformen ist jeder Funktion oder jedem Verfahren ein Symbol oder eine andere grafische Darstellung zugeordnet. Der Programmierer kann gewünschte Funktionen oder Verfahren aus der Bibliothek wählen, z. B. durch Ziehen und Ablegen der jeweiligen Symbole auf den Zeichenbereich, und sie dann verbinden, z. B. indem er Linien zwischen ihnen zieht, um den Signalfluss vorzugeben. In alternativen Ausführungsformen gibt der Programmierer den Signalfluss unter Verwendung generischer Formen und Symbole vor und ordnet die Funktionen danach den verschiedenen Symbolen zu.
-
Die Funktionen können für einen bestimmten Prozessor oder eine bestimmte Hardwareimplementierung optimiert werden. Tatsächlich werden in manchen Ausführungsformen mehrere Versionen ausführbaren Codes, die für verschiedene Hardwareimplementierungen optimiert sind, für die gleichen Funktionalitäten bereitgestellt, so dass der Programmentwickler aus ihnen auswählen kann. Ferner können die Funktionen oder Verfahren an sich für bestimmte Eingabeblockgrößen und Ausgabeeinheitsgrößen programmiert werden. Alternativ können die Eingabe- und Ausgabeblöcke für jede Funktion oder jedes Verfahren in der Größe variieren, so dass der Programmierer ihre Größe aufgrund der jeweiligen Anwendung bestimmen kann. In manchen Ausführungsformen umfasst die Bibliothek sowohl Funktionen für Datenblöcke mit festgelegter Größe als auch Datenblöcke mit variabler Größe. Zusätzlich zur Blockgröße können den verschiedenen Funktionsblöcken auch andere vom Benutzer wählbare Parameter zugewiesen werden.
-
Das GUI-Werkzeug 500 umfasst ferner einen Compiler 510 zum automatischen Generieren von Programmcode 512, der den gewünschten Signalfluss aus der grafischen Abbildung implementiert. Der Compiler 510 gliedert die geeigneten Funktionen aus der Bibliothek 508 ein, indem sie sie z. B. mit dem Programmcode 512 verknüpft oder direkt in ihn kopiert, und fügt die notwendigen Anweisungen hinzu, die die Datenbewegung zwischen den Knoten steuern. Der Compiler 508 kann einen Regelsatz zur Übersetzung von grafischen Elementen, die z. B. Verbindungen, Schalter und Puffer darstellen, in geeignete ausführbare Anweisungen umfassen. In manchen Ausführungsformen ist das GUI-Werkzeug dazu in der Lage, mehrere Programmiersprachen zu unterstützen; in diesem Fall umfasst die Bibliothek 508 Programmcode für jede Funktion in jeder der unterstützten Sprachen. In bestimmten Ausführungsformen umfasst das GUI-Werkzeug auch einen Simulator 514, mit dem der Programmierer den bestimmten Signalfluss testen kann, z. B. um bestimmte Leistungsparameter (wie z. B. Speicheranforderungen, die Ausführungszeit auf einem bestimmten Prozessor, Verarbeitungslatenzen usw.) testen kann. Der Simulator 514 kann mit dem Compiler 510 integriert sein.
-
6A–6H stellen eine beispielhafte GUI zum Programmieren von Signalfluss gemäß einer Ausführungsform dar. Die GUI umfasst eine Tafel 600 aus Registerkarten neben dem Zeichenbereich 504. Eine Registerkarte „Formen” 602 kann verschiedene grafische Elemente wie Linien, Rechtecke usw. umfassen, um den Signalfluss schematisch zu zeichnen. Jeder der Knoten (wie dargestellt, z. B. als Rechtecke) in der Schemazeichnung kann einer beliebigen Bildverarbeitungsfunktion (oder einer anderen Funktionsart) zugeordnet werden, die in der Registerkarte „IP-Blöcke” 604 verfügbar sind. Wie in 6B gezeigt, können diese Funktionen beispielsweise als Dropdown-Liste 605 bereitgestellt werden. Nachdem die Bildverarbeitungsfunktion einem Knoten zugeordnet wurde, können die Parameter für die Funktion in ein Parameterfenster für diese Funktion eingegeben werden. Parameterfenster werden in der GUI im Zusammenhang mit den verschiedenen Funktionen implementiert, die in der Bibliothek verfügbar sind; 6C zeigt beispielhafte Parameterfenster 608, 610, 612 für drei verschiedene Bildbearbeitungsfunktionalitäten.
-
Die Parameter, die vom Benutzer im Parameterfenster 606, 608, 610 festgelegt werden (oder, wenn nicht festgelegt, vorgegebene Werte) werden in einer Parameterliste oder einem Parameterfeld gespeichert (z. B. einem Doppelzeiger-Feld oder einer verknüpften Liste) und an den Compiler 510 weitergeleitet. Gewöhnlich weist jeder Knoten einen oder mehrere Parameter auf, die in der Liste gespeichert werden. Beispielsweise werden bezugnehmend auf 6C, wenn der Knoten 0 für „Schwellenwertbildung” und Knoten 1 für „Erosion” steht, der erste Eintrag in der Parameterliste aus dem Parameterfenster 612 für Knoten 0 und die nächsten drei Einträge vom Parameterfenster 610 für Knoten 1 gespeichert. Der Compiler 510 verwendet die Parameterliste, um Werte der Bildverarbeitungsfunktion zurückzugeben, die dem jeweiligen Knoten zugeordnet sind; diese zurückgegebenen Werte können ebenfalls in einer Liste oder einem Feld gespeichert werden.
-
Die Tafel 600 umfasst ferner eine DMA-Registerkarte 620, die der Entwickler dazu verwenden kann, DMA-Bewegung und -Planung grafisch vorzugeben, wie in 6D–6F gezeigt. Den grafischen DMA-Elementen, wie DMA-Quellknoten und DMA-Senkeknoten 622, 624 und DMA-Quellplanungspfaden und DMA-Senkeplanungspfaden 626, 628 sind Parameter zugeordnet, die anschließend vom Compiler 510 verwendet werden, um geeigneten codesteuernden DMA zu generieren. Einige dieser Parameter können direkt vom grafischen Signalfluss gelesen werden (z. B. dem Knoten im Signalfluss, mit dem ein DMA-Knoten verbunden ist), während andere vom Entwickler in ein Parameterfenster eingegeben werden, das sich öffnet, wenn das grafische DMA-Element gewählt wird. Beispielsweise kann der Compiler 510 aufgrund der Benutzereingabe in den Parameterfenstern 630, 632 für die Knoten 0 und 1 zwei Registereinträge in einem Speicherfeld für jeden Knoten einrichten, wie in 6H gezeigt. Insbesondere kann der Compiler 510 Speicher für Puffer mit dem Namen „Puffername” zuweisen, die die Dimensionen „Pufferbreite” und „Pufferhöhe” aufweisen. Dann kann er zwei Registereinträge in einem Feld oder einer Liste 634 für diesen Knoten einrichten; der erste Eintrag ist die Adresse, die automatisch aus den Parametern „Puffername” und „Offset” generiert wird, und der zweite Eintrag basiert auf „DMA-Schritt”. Diese Listen werden dazu verwendet, die verschiedenen DMA-Pfade durch den Compiler 510 einzurichten, zu beginnen und zu beenden. Daher ist in verschiedenen Ausführungsformen hierin DMA ein integraler Bestandteil der GUI, und Code für DMA-Bewegung und DMA-Planung wird von der grafischen und/oder textbasierten Benutzereingabe automatisch generiert und stellt den Benutzer von dieser andernfalls mühsamen Aufgabe frei.
-
Die Eingabe, die vom Entwickler zum Generieren von DMA-Code gefordert wird, umfasst gewöhnlich eine Pufferadresse für externe Speicher für jeden Quell- oder Senkeknoten, den Schritt zum Übergang zur nächsten Zeile oder den nächsten Zeilen des Bild-/Videopuffers, und wahlweise die Planung für einen oder mehrere Quell-/Senkeknoten sowie die Verarbeitungsknoten, die jedem Planungspfad zugeordnet sind. Legt der Entwickler die Planung nicht fest, kann der Compiler 510 basierend auf vorgegebenen Regeln automatisch einen DMA-Planungspfad generieren. Der Compiler kann dem Quellport eines Knotens auch automatisch Dual-Pingpongpuffer zuordnen, wenn dieser Port parallele, überlappende Eingabe von einem DMA-Knoten und dem diesem Knoten zugeordneten Eingabepuffer empfängt.
-
DMA-Planung ist ferner in 6D–6F für einen beispielhaften Signalfluss für eine Ausführungsform dargestellt, in der ein einziger Prozessor die Knoten wiederholt durchläuft. Hierin sind die Knoten 0 und 1 DMA-Quellknoten und die Knoten 4 und 5 den DMA-Senkeknoten zugeordnet. Die DMA-Quellknoten leiten Daten vom externen Speicher in die internen Puffer, und die Senkeknoten führen Daten aus den Puffer heraus zum externen Speicher. Jeder DMA-Knoten ist entweder mit einem Quellport oder einem Ausgabeport eines Verarbeitungsknotens verbunden. Der Entwickler kann die Planungspfade der DMA-Quellen und DMA-Senken festlegen (andernfalls findet der Compiler 510 automatisch geeignete Planungspfade). Es kann ein einziger Pfad 626 für alle DMA-Quellknoten und ein Pfad 628 für alle DMA-Senkeknoten vorliegen. Die Pfade geben an, wenn der DMA beginnt und endet; beispielsweise beginnt Quellknoten zugeordneter DMA vor Knoten 3 und endet nach Knoten 5, d. h. neue Daten kommen durch DMA herein, während die Knoten 3 bis 5 verarbeiten, während Ausgabe über DMA ausgeschrieben wird, während die Knoten 1 bis 2 arbeiten. Ähnlich beginnt DMA für Senkeknoten vor Knoten 0 und endet nach Knoten 2. Diese Art von Planung ist für Ausführungsformen typisch, in denen allen DMA-Knoten im Signalfluss ein einziger DMA-Controller (oder DMA-befähigende Hardware/Periphergeräte) zugeordnet ist.
-
In alternativen Ausführungsformen können den DMA-Knoten mehrere DMA-Controller zugeordnet sein; in solchen Fällen können die DMA-Pfade überlappen, wie in 6E gezeigt. In einem noch anderen Fall, gezeigt in 6F, überlappen der DMA am Quellport 1 und der Puffer am Quellport 2 von Knoten 1, d. h., wenn DMA Daten aus der nächsten Zeile in den Puffer am Quellport 2 einbringt, verarbeitet Knoten 1 auch die Daten im Puffer am Quellport 2 für die vorherige Zeile. Dies führt zu Datenparallelität oder Datenkorruption im Puffer am Quellport 2 für Knoten 1. Ein Dual-Zustand Pingpong-Puffer (wie sie dem Fachmann bekannt sind) am Quellport 2 von Knoten 1 löst dieses Problem, indem die Verarbeitung von Daten durch Knoten 1 und DMA-Eingabe an Knoten 1 parallel, jedoch unabhängig ablaufen können. Der Compiler 510 kann das Vorkommen eines solchen Überlappens automatisch feststellen und den betroffenen Quellports den Dual-Zustand-Pingpongpuffer zuweisen.
-
Ein wie oben beschriebenes GUI-Werkzeug 500 kann beispielsweise in Software implementiert werden, die auf einem Computer für allgemeine Zwecke ausgeführt wird. 7 zeigt eine beispielhafte Ausführungsform eines Computers, einschließlich einer Zentraleinheit CPU 700 und eines zugeordneten Systemspeichers 702, einer oder mehrerer nichtflüchtiger Massenspeichervorrichtungen (und zugeordneter Gerätetreiber) 704, Eingabe-/Ausgabevorrichtungen 706 (wie z. B. einen Bildschirm, eine Tastatur, eine Maus, einen Stylus usw.) und eines Systembus 708 über den der Prozessor und der Speicher miteinander und mit anderen Systemkomponenten kommunizieren können. Der Systemspeicher 702 speichert Anweisungen, konzeptionell als Modulgruppen dargestellt, die den Betrieb der CPU 700 und seine Interaktion mit den anderen Hardwarekomponenten steuert. Ein Betriebssystem 710 leitet die Ausführung von grundlegenden Systemfunktionen auf niedriger Ebene, wie Speicherzuweisung, Dateimanagement und den Betrieb der Speichervorrichtungen 704. Auf einer höheren Ebene stellen eine oder mehrere Dienstanwendungen die Rechnerfunktionalität zum automatischen Generieren von Code aufgrund einer grafischen Signalflussdarstellung bereit. Diese Anwendungen können den Editor 502, Compiler 510 und Simulator 514 umfassen. Selbstverständlich können diese Module kombiniert, weiter partitioniert oder anders organisiert werden; wie der Fachmann versteht, können die Anweisungen allgemein auf viele verschiedene Weisen gruppiert und organisiert werden. Der Systemspeicher 702 kann auch die Bibliothek 508 von Verarbeitungsblöcken speichern. Die Anweisungen, die die Anwendungen 502, 510, 514 implementieren, können in einer von mehreren verschiedenen geeigneten Programmiersprachen programmiert werden, insbesondere C, C++, Basic, Pascal, Fortran oder einer Assemblersprache.
-
Die hierin verwendeten Begriffe und Ausdrücke werden als beschreibende und nicht als einschränkende Begriffe und Ausdrücke benutzt, und es ist nicht beabsichtigt, Entsprechungen der gezeigten und beschriebenen Merkmale oder Teile davon durch die Verwendung solcher Begriffe und Ausdrücke auszuschließen. Außerdem ist es, nachdem bestimmte Ausführungsformen der Erfindung beschrieben wurden, für den Durchschnittsfachmann offensichtlich, dass andere Ausführungsformen, die die hierin offenbarten Konzepte enthalten, verwendet werden können, ohne vom Geist und Rahmen der Erfindung abzuweichen. Dementsprechend dürfen die beschriebenen Ausführungsformen in jeder Hinsicht nur als illustrativ und nicht als einschränkend angesehen werden.