sahin7755
Posts: 2
Joined: Fri Jun 21, 2019 6:09 pm

Face Tracking OpenCv, Raspberry Pi, Arduino

Fri Jun 21, 2019 6:21 pm

my project is face tracking. Image processing part and coordinate import is OK. but I can't send these coordinates to the arduino. serial communication was working properly when I sent "hello world". I can't send coordinates right now. I need your help.
python code

Code: Select all

import numpy as np
import serial
import time
import sys
import cv2

arduino = serial.Serial('/dev/ttyUSB1', 9600)
if(arduino.isOpen() == False):
    arduino.open()
time.sleep(2)
print("Connection to arduino...")



face_cascade = cv2.CascadeClassifier('lbpcascade_frontalface.xml')

cap = cv2.VideoCapture(0)

while 1:
    ret, img = cap.read()
    cv2.line(img,(500,250),(0,250),(0,255,0),1)
    cv2.line(img,(250,0),(250,500),(0,255,0),1)
    cv2.circle(img, (250, 250), 5, (255, 255, 255), -1)
    gray  = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    faces = face_cascade.detectMultiScale(gray, 1.3)

    for (x,y,w,h) in faces:
        cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),5)
        roi_gray  = gray[y:y+h, x:x+w]
        roi_color = img[y:y+h, x:x+w]

        arr = {y:y+h, x:x+w}
        print (arr)
        
        print ('X :' +str(x))
        print ('Y :'+str(y))
        print ('x+w :' +str(x+w))
        print ('y+h :' +str(y+h))

        xx = int(x+(x+h))/2
        yy = int(y+(y+w))/2

        print (xx)
        print (yy)

        center = (xx,yy)

        print("Center of Rectangle is :", center)
        data = "X{0:f}Y{1:f}Z".format(xx, yy)
        print ("output = '" +data+ "'")
        #send  coordinates

        arduino.write(data)
    

        cv2.imshow('img',img)
   
        k = cv2.waitKey(30) & 0xff
        if k == 27:
            break

Arduino;
#include <Servo.h>  

#define  servomaxx   180   // max degree servo horizontal (x) can turn
#define  servomaxy   180   // max degree servo vertical (y) can turn
#define  screenmaxx   320   // max screen horizontal (x)resolution
#define  screenmaxy   240    // max screen vertical (y) resolution
#define  servocenterx   85  // center po#define  of x servo
#define  servocentery   60  // center po#define  of y servo
#define  servopinx   9   // digital pin for servo x
#define  servopiny   10  // digital servo for pin y
#define  baudrate 9600  // com port speed. Must match your C++ setting
#define distancex 1  // x servo rotation steps
#define distancey 1  // y servo rotation steps

int valx = 0;       // store x data from serial port
int valy = 0;       // store y data from serial port
int posx = 0;
int posy = 0;
int incx = 10;  // significant increments of horizontal (x) camera movement
int incy = 10;  // significant increments of vertical (y) camera movement

Servo servox;
Servo servoy;

short X = 0;  // to build  2 byte integer from serial in byte
short Y = 0;  // to build  2 byte integer from serial in byte
int   XY = 0;  //to build  2 byte integer from serial in byte

void setup() {

  Serial.begin(baudrate);        // connect to the serial port
  Serial.println("Starting Cam-servo Face tracker");

  pinMode(servopinx,OUTPUT);    // declare the LED's pin as output
  pinMode(servopiny,OUTPUT);    // declare the LED's pin as output

  servoy.attach(servopiny); 
  servox.attach(servopinx); 

  // center servos

  servox.write(servocenterx); 
  delay(200);
  servoy.write(servocentery); 
  delay(200);
}

void loop () {
  while(Serial.available() <=0); // wait for incoming serial data
  if (Serial.available() >= 4)  // wait for 4 bytes. 
  {
    // get X axis 2-byte integer from serial
    X = Serial.read();
    delay(5);
    Y = Serial.read();
    XY =word(X,Y);
    valx = XY; 
    delay(5);

    // get Y axis 2-byte integer from serial
    X = Serial.read();
    delay(5);
    Y = Serial.read();
    XY = word(X, Y);
    valy = XY; 
    delay(5);

    // read last servos positions
    posx = servox.read(); 
    posy = servoy.read();

    //Find out if the X component of the face is to the left of the middle of the screen.
    if(valx < (screenmaxx/2 - incx)){
      if( posx >= incx ) posx += distancex; //Update the pan position variable to move the servo to the left.
    }
    //Find out if the X component of the face is to the right of the middle of the screen.
    else if(valx > screenmaxx/2 + incx){
      if(posx <= servomaxx-incx) posx -=distancex; //Update the pan position variable to move the servo to the right.
    }

    //Find out if the Y component of the face is below the middle of the screen.
    if(valy < (screenmaxy/2 - incy)){
      if(posy >= 5)posy += distancey; //If it is below the middle of the screen, update the tilt position variable to lower the tilt servo.
    }
    //Find out if the Y component of the face is above the middle of the screen.
    else if(valy > (screenmaxy/2 + incy)){
      if(posy <= 175)posy -= distancey; //Update the tilt position variable to raise the tilt servo.
    }

    // Servos will rotate accordingly 
    servox.write(posx);
    servoy.write(posy);

  }   
} 
How do I send X, Y coordinates?

ghp
Posts: 1411
Joined: Wed Jun 12, 2013 12:41 pm
Location: Stuttgart Germany
Contact: Website

Re: Face Tracking OpenCv, Raspberry Pi, Arduino

Fri Jun 21, 2019 9:34 pm

Hello,
please observe that pasting python code needs to use the 'code'-tags which preserve identation.
There is a sequence:

Code: Select all

data = "X{0:f}Y{1:f}Z".format(xx, yy)
print ("output = '" +data+ "'")
#send coordinates

arduino.write(data)
Would be great to see the console output of this print statement, as this string needs to be decoded by the arduino. Pls paste this here.

sahin7755
Posts: 2
Joined: Fri Jun 21, 2019 6:09 pm

Re: Face Tracking OpenCv, Raspberry Pi, Arduino

Fri Jun 21, 2019 10:58 pm

ghp wrote:
Fri Jun 21, 2019 9:34 pm
Hello,
please observe that pasting python code needs to use the 'code'-tags which preserve identation.
There is a sequence:

Code: Select all

data = "X{0:f}Y{1:f}Z".format(xx, yy)
print ("output = '" +data+ "'")
#send coordinates

arduino.write(data)
Would be great to see the console output of this print statement, as this string needs to be decoded by the arduino. Pls paste this here.

Code: Select all

Connection to Arduino
{281:365,286:370}
X :286
Y :281
x+w :370
y+h :365
328.0
323.0
Center of rectangle is: (328.0,323.0)
output ='X328.000000Y323.000000Z'
File face.py line 53, in <module>
arduino.write(data)
File .......
d= to_bytes(data)
File.....
raise TypeError ( unicode strings are not supported, please encode to bytes:{!r'.format(seq)}
TypeError : unicode strings are not supported, please encode bytes : X328.000000Y323.000000Z


ghp
Posts: 1411
Joined: Wed Jun 12, 2013 12:41 pm
Location: Stuttgart Germany
Contact: Website

Re: Face Tracking OpenCv, Raspberry Pi, Arduino

Sat Jun 22, 2019 4:32 am

There are multiple places where things could be improved.
- Type Error,
- data formatting,
- Reading data on arduino side.

The TypeError is thrown as you pass a string object to the arduino.write method. Writing to serial needs a bytes like object.
Here you can convert by adding the encode-method of the string class.
arduino.write( data.encode('ascii') )

Data formatting. Data on arduino side are obviously expected as integer. Your data sent are formatted as float.
output ='X328.000000Y323.000000Z'
you make things easier when formatting data in raspberry side in the format needed on microcontroller side. Think that
data = "X{0:d}Y{1:d}Z".format( int(xx), int(yy) )
would send data then as
output ='X328Y323Z'
which makes it shorter and easier to decode on arduino.

Arduino side, reading the data. Serial.read only gets one byte of input, it does not convert multiple bytes into an integer. This needs to be done by the sketch. There are many approaches do do this. Simple is:
- define a data array which is filled with the incoming data, started at X until the terminating Z.
- in the loop(), fill this array. When a Z was found, decode the content.

Code: Select all

uint8_t data_buffer[ 3 + // x,y,z
                                    2 * 5 + // two integer, possible range 0...65535
                                    1 ];    // one extra byte for a terminating zero
const uint8_t START = 0;
const uint8_t READING = 1;

uint8_t read_state =START;
uint8_t data_buffer_index =0;

in loop:
loop(){

   uint8_t new_data_available = 0;
   
    if (Serial.available()){
        uint8_t b = Serial.read();
        if ( read_state == START){
             if ( b == 'X'){
                 data_buffer_index = 0;
                 data_buffer[ data_buffer_index] = b;
                 data_buffer_index ++;
                 read_state = READING;
            } else {
                 // ignore other char
            }
        }  else if ( read_state == READING ){
                 data_buffer[ data_buffer_index] = b;
                 data_buffer_index ++;
                 if (data_buffer_index >= sizeof ( data_buffer)){
                     // ups, too much data
                     read_state = START;
                }
               else if ( b == 'Z'){
                     // add an extra '\0' to make the buffer a c-string
                     data_buffer[ data_buffer_index] = '\0';
                     data_buffer_index ++;
                    
                    // decode_data reads the data_buffer, places x,y in variables and returns 1 if successful.
                    new_data_available =  decode_data ();
                     read_state = START;
               }
        }
   }

  if ( new_data_available){
  
       // proxess x, y
       ...
  
  	new_data_available = 0;
  }

   // do other things
}
(code not tested.)

The decode_data-method can now read the buffer and place x, y in variables. The sscanf-function could help.

Return to “Python”