Implementierung eines Sobel-Filters in C und Assembler Kamerabasierte Verkehrszeichen-Erkennung

Die Nachfrage nach kamerabasierten Fahrerassistenz-Systemen in der Automobilindustrie wird immer größer. Diese Applikationen gehören in den Bereich der Mustererkennung und müssen auf energiesparenden und preisgünstigen digitalen Signalprozessoren (DSP) ausführbar sein.

Implementierung eines Sobel-Filters in C und Assembler

Die Nachfrage nach kamerabasierten Fahrerassistenz-Systemen in der Automobilindustrie wird immer größer. Diese Applikationen gehören in den Bereich der Mustererkennung und müssen auf energiesparenden und preisgünstigen digitalen Signalprozessoren (DSP) ausführbar sein. Im Folgenden werden an Hand einer kamerabasierten Verkehrszeichen-Erkennung, die auf dem Blackfin-Prozessor von Analog Devices läuft, unterschiedliche Weisen für die Implementierung eines Sobel-Filters aufgezeigt. Die erzielten Ergebnisse werden verglichen und Schritte gezeigt, um die verfügbare Rechenleistung optimal zu nutzen.

Im ersten Teil wurde ein Systemkonzept zur visuellen Bildverarbeitung vorgestellt. Es besteht aus einer geeigneten Hardware und einem Software-Gerüst. Das System-Block-Diagramm der Hardware veranschaulichte, dass schon mit minimalen Komponenten (CMOS-Sensor, Blackfin-Prozessor, TFT-Bildschirm, SDRAM) ein System realisiert werden kann. Das vorgestellte Software-Gerüst zeigte, dass mit Hilfe von DMAKanälen nicht nur Datentransfers parallelisiert werden, sondern Datenbereiche auch selektiver ausgewählt werden können (Laden von Bildausschnitten). Zusätzlich können Datenbereiche neu organisiert werden (drehen oder verschachteln von Bildausschnitten).

Das Sobel-Filter ist meist am Anfang einer Verarbeitungskette zu finden. Das Ergebnis der Berechung soll die Stärke von Kanten und deren Richtung aufzeigen (Vektoren). Dies wird mittels zweidimensionaler Faltung erreicht. Bild 1 zeigt die Operation einer klassischen Faltung. Um einen Ergebnispunkt G22 zu berechnen, wird eine 3×3-Filtermatrix H über das Eingangsbild F gelegt und jeder Punkt der Matrix H mit jeweils einem Bildpunkt multipliziert und anschließend akkumuliert. Die 3×3-Filtermatrix H wandert nun über das gesamte Bild, um aus allen anderen Bildpunkten auch Ergebnispunkte G zu berechnen. Gleichung (1) zeigt die Operation einer Faltung (Kasten).

Listing 1 - Sobel-Standard-Implementierung in C

int i ;
short a,b,c;

a=(nLineCount) %4;
b=(nLineCount+1) %4;
c=(nLineCount+2) %4;

for (i=0; i<192; i++ ){
   gxy[i*2+1] = uiInputLines[c] [i+2] – uiInputLines[a] [i]
   – (uiInputLines[b] [i] * 2)
   + uiInputLines[a] [i+2] – uiInputLines[c] [i]
   + (uiInputLines[b] [i+2] * 2);

   gxy[2*i] = uiInputLines[c] [i+2] – uiInputLines[a] [i]
   + (uiInputLines[c] [i+1] * 2)
   – (uiInputLines[a] [i+2] + uiInputLines[c] [i])
   – (uiInputLines[a] [i+1] * 2);
}

 

Listing 2 -. 2-D-Faltung in C:

for (i=0; i<256; i += 3 ) {
   for (j=0; j < 9 ; j++) {
      *pGx += pSource[i+j]*SobelMatX[j];
      *pGy += pSource[i+j]*SobelMatY[j];
   }
}

 

Listing 3 - Datenoptimierte 2-D-Faltung in C

for (i=0; i<256; i += 3 ){
   #pragma no_alias
   #pragma vector_for
   for (j=0; j < 9 ; j++) {
      *pGx += pSource[i+j]*SobelMatXY[j];
      *pGy += pSource[i+j]*SobelMatXY[j+1];
   }
}

 

Listing 4 - Faltung inklusive geometrischer Addition mittels Assembler-Code

.global _SobelASM;
.section L1_data_b;
.byte2 ConvMatXY[] = {–1, –1, 0, –2, 1, –1, –2, 0, 0, 0, 2, 0, –1, 1, 0, 2, 1, 1};
.section program;

_SobelASM;
   [– –SP] = (R7:0, P5:0);
   p4 = r1;
   p5 = r2;
   r4 = 384(z);

   r5 = r0;
   r6 = r5+r4;
   r7 = r6+r4;

   i1 = r5; i2 = r6; i3 = r7;
   i0.l=ConvMatXY; i0.h=ConvMatXY; m0.l=4; m0.h=0; l0.l=36; l0.h=0; b0=i0;

   p0=256;
   MNOP || r0 = [i0++] || r1.l = w[i1++];
   LSETUP (lStart, lEnd) LC0 = P0;
   lStart: a1 = r0.h*r1.l, a0 = r0.l*r1.l (IS) || r0 = [i0++] || r1.l = w[i1++];
      a1 += r0.h*r1.l, a0 += r0.l*r1.l (IS) || r0 = [i0++] || r1.l = w[i1–];
      a1 += r0.h*r1.l, a0 += r0.l*r1.l (IS) || r0 = [i0++] || r1.l = w[i2++];
      a1 += r0.h*r1.l, a0 += r0.l*r1.l (IS) || r0 = [i0++] || r1.l = w[i2++];
      r6.l = r5.h+r5.l(s) || r0 = [i0++] || r1.l = w[i2–];
      a1 += r0.h*r1.l, a0 += r0.l*r1.l (IS) || r0 = [i0++] || r1.l = w[i3++];
      a1 += r0.h*r1.l, a0 += r0.l*r1.l (IS) || r0 = [i0++] || r1.l = w[i3++];
      a1 += r0.h*r1.l, a0 += r0.l*r1.l (IS) || r0 = [i0++] || r1.l = w[i3–];
      r4.h = (a1 += r0.h*r1.l), r4.l = (a0 += r0.l*r1.l) (IS) || r0 = [i0++] || r1.l = w[i1++];
   lEnd: r5.h=r4.h*r4.h, r5.l=r4.l*r4.l(IS) || w[p4++] = r6;
   (R7:0, P5:0) = [SP++];
RTS;
_SobelASM.END;

 

Listing 5 - Winkel-Berechnung mittels der Funktion atan2_fr16

void SobelAngle ( signed short pInputXY[], fract16 pOutputPhaseXY[]) {
   int nCount;
   for(nCount=0;nCount       pOutputPhaseXY[nCount] = atan2_fr16(pInputXY[nCount*2+1], pInputXY[nCount*2]);
   }
}

 

Listing 6 - Winkel-Berechnung mittels Arkus-Tangens-Tabelle

void SobelAngleLUT(signed short pInputXY[], signed short pInput2XY[], fract16 pOutputPhaseXY[]){
   char bit2shift;
   short max_G;
   int nCount;
   for(nCount=0;nCount       max_G = max(abs(pInputXY[nCount*2+1]),abs(pInputXY[nCount*2]));
      asm(„%0.l=signbits %0.l;“ :“=d“ (bit2shift) : „d“ (max_G) : „r7“);
      bit2shift = 15 – bit2shift;
      if(bit2shift>7){
         bit2shift = bit2shift – 7;
         pInputXY[nCount*2] = pInputXY[nCount*2]>bit2shift;
         pInputXY[nCount*2+1] = pInputXY[nCount*2+1]>bit2shift;
      }
      pOutputPhaseXY[nCount] = AngleLUT[pInputXY[nCount*2+1]+ANGLUT_SIZE] [pInputXY[nCount*2]+ANGLUT_SIZE];
   }
}

 

Listing 7 - Faltung inklusive geometrische Addition und Winkel-Ermittlung mittels Assembler-Code

_SobelAngleASM;
   p1 = [sp+0xc];
   p3 = [sp+0x10];
   [–SP] =(R7:0, P5:0=;
   p4 = r1;
   p5 = r2;
   r1 = -16;
   [SP–] = r1;
   r1 = 9;
   [SP++] = r1;
   r3 = 384(z);
   r5 = r0;
   r6 = r5+r3;
   r7 = r6+r3;
   r3 = ANGLUT_SIZE;
   i1 = r5; i2 = r6; i3 = r7;
   i0.l=ConvMatXY; i0.h=ConvMatXY; m0.l=4; m0.h=0; l0.l=32; l0.h=0; b0=i0;
   p0=LINE_LENGTH;
   MNOP || r0 = [i0++] || r1.l = w[i1++];
   LSETUP (lStart, lEnd) LC0 = P0;
   lStart: a1 = r0.h*r1.l, a0 = r0.l*r1.l (IS) || r0 = [i0++] || r1.l = w[i1++];
         a1 += r0.h*r1.l, a0 += r0.l*r1.l (IS) || r0 = [i0++] || r1.l = w[i1–];
         a1 += r0.h*r1.l, a0 += r0.l*r1.l (IS) || r0 = [i0++] || r1.l = w[i2++];
         a1 += r0.h*r1.l, a0 += r0.l*r1.l (IS) || w[p5++] = r5 || r1.l = w[i2++];
         r5.l = r4.h+r4.l(s) || r0 = [i0++] || r1.l = w[i2–];
         a1 += r0.h*r1.l, a0 += r0.l*r1.l (IS) || r0 = [i0++] || r1.l = w[i3++];
         a1 += r0.h*r1.l, a0 += r0.l*r1.l (IS) || r0 = [i0++] || r1.l = w[i3++];
         a1 += r0.h*r1.l, a0 += r0.l*r1.l (IS) || r0 = [i0++] || r1.l = w[i3–];
         r4.h = (a1 += r0.h*r1.l), r4.l = (a0 += r0.l*r1.l) (IS) || r0 = [i0++] || r1.l = w[i1++];
         r6 = abs r4(v) || r2 = [SP–];
         r7 = lshift r6 by r2.l;
         r2 = max (r6,r7) (v);
         r7.l = signbits r2.l || re = [SP++];

         r7.l = r7.l – r2.l(s);
         r6.l = ashift r4.l by r7.l;
         r6.h = ashift r4.h by r7.l;
         r7.h = r6.h + r3.l(s);
         r7.l = r6.l + r3.l(s);
         r7 = r7.h * r7.l(IS) || [p4++] = r4;
         p2 = r7;
         p2 = p1 + p2;
         r2 = b[p2](z);
         r4.h=r4.h*r4.h, r4.l=r4.l*r4.l(IS) || w[p3++] = r2;
   lEnd: NOP;
   (R7:0, P5:0) = [SP++];
RTS;
_SobelAngleASM.END;