Skip to content

6. Rozpoznanie osoby podľa oblečenia

Granko15 edited this page Jun 5, 2023 · 12 revisions

Rozpoznanie osoby podľa oblečenia

Táto ukážka znázorňuje rozpoznanie osoby pomocou počítačového videnia, klasifikáciu obrázku a spätnú väzbu vo forme audia.

Spustenie ukážky

Na spustenie ukážky musíme cez terminál najskôr spustiť demo.launch súbor, ktorý sme si pripravili.

roslaunch matus_showcase demo.launch

Zároveň musíme na našom Raspberry, ktoré sme si správne nakonfigurovali spustiť python skript receive_image.py.

./receive_image.py

Potom môžeme spustiť na Jupiterovi:

rosrun matus_showcase person_recognizer.py
rosrun matus_showcase take_picture.py
rosrun matus_showcase send_image.py

Pri spustení uzla take_picture paramater nemusíme uviesť nakoľko vrchná kamera je nastavená ako predvolená. Pri tomto spustení si robot vypýta meno osoby ak ju ešte nepozná. Pri každom ďalšom spustení uzla rosrun matus_showcase send_image.py už robot povie meno osoby alebo si vypýta meno.


Neurónová sieť v obraze rozpoznala okuliare ako je vidno v spodnom termináli. Vpravo dolu je vypísané glasses.

rozponavanie oblecenia

Pohľad cez vrchnú kameru.

vrchná kamera

Raspberry Pi 4 s Google Coral USB akcelerátorom zapojeným cez USB3.0.

raspberry_coral

Video ukážka

Video s rozpoznávaním nájdete TU

Implementácia

Architektúra tohto programu sa skladá z piatich komponentov a to uzlu na odfotenie obrazu, komponentu na odoslanie obrázka, komponentu na prijatie obrázka, komponentu na klasifikáciu obrázka a komponentu na rozpoznanie osoby.

Uzol na odfotenie obrazu kamery

Tento uzol máme podrobne popísaný TU. Uzol v tomto programe voláme bez parametrov, pretože chcem použiť vrchnú kameru robota, ktorá je predvolená automaticky.

Komponent na odoslanie obrázka

Tento kód vytvára spojenie s Raspberry Pi pomocou TCP/IP a odosiela obrazové dáta na danú IP adresu a port. Popíšme si jednotlivé časti kódu:

        # Specify the IP address and port number of the receiver
        # Specify the path of the image you want to send
        self.IP_ADDRESS = '192.168.8.5'
        self.PORT = 7123
        self.path = '/home/mustar/jupiter/matus/matus_showcase/images/photo.png'
        self.image_data = None

S inicializáciou triedy SendImage nastavíme IP adresu Raspberry Pi z konfiguračného súboru, PORT a cestu k obrázku.


    def load_image(self, path):
        with open(path, 'rb') as f:
            self.image_data = f.read()

Metóda slúži na načítanie dát obrázka.


    def send_image(self):
        self.load_image(self.path)

        # Create a TCP/IP socket
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        # Connect to the receiver
        self.sock.connect((self.IP_ADDRESS, self.PORT))

        # Send the size of the image data as a 4-byte integer
        image_size = struct.pack("!L", len(self.image_data))
        self.sock.sendall(image_size)

        # Send the image data
        self.sock.sendall(self.image_data)

        # Wait for the answer
        answer = self.sock.recv(4096)
        print(answer)
        
        # Publish a message to /clothes topic
        ClothingPublisher(answer)

        # Say the answer out loud
        talker = Talker(answer)
        talker.talk()

        # Close the socket
        self.sock.close()

Hlavná metóda, ktorá odosiela obrázok.

  • Najprv načítame obrazové dáta.
  • Vytvoríme TCP/IP socket, ktorý naviaže spojenie s Raspberry cez IP aresu a port.
  • Najprv odošleme veľkosť obrázka ako štvorbajtové číslo a následne odošleme samotné obrazové dáta.
  • Čakáme na odpoveď od príjemcu, ktorú publikujeme na topic "clothes" pomocou "ClothingPublisher".
  • Socket uzavrie spojenie.

Komponent na prijatie obrázka

Tento komponent sa nachádza na Raspberry Pi a slúži na prijímanie obrazových dát z robota Jupiter a ich následné spracovanie.

    def __init__(self):
        # Specify the IP address and port number of the sender
        # Specify the path of the image save destination
        self.IP_ADDRESS = '0.0.0.0'
        self.PORT = 7123
        self.save_path = '/home/pi/jupiter/clothes/received.png'

Pri inicializácii triedy ImageReceiver nastavíme IP adresu robota Jupiter, PORT a cestu na uloženie obrázka.


    def receive_image(self):
        # Create a TCP/IP socket
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
            # Bind socket to IP address and port number
            sock.bind((self.IP_ADDRESS, self.PORT))
            sock.listen(1)

            while True:
                # Wait for a connection
                print("Waiting for connection>>")
                conn, addr = sock.accept()
                print('Connected by', addr)

                # Receive the size of the image data as a 4-byte integer
                size_bytes = conn.recv(4)
                size = int.from_bytes(size_bytes, byteorder='big')

                # Receive the image data
                data = b''
                while len(data) < size:
                    packet = conn.recv(size - len(data))
                    if not packet:
                        break
                    data += packet

                # Save the image data to a file
                with open(self.save_path, 'wb') as f:
                    f.write(data)
                              
                result = classification()
                
                # Send the classification result as answer
                conn.send(result.encode("utf-8"))
                print(result)

                # Close the connection
                conn.close()

Hlavná funkcionalita programu sa vykonáva v tejto metóde.

  • Vytvorí sa TCP/IP socket, ktorý vytvorí spojenie na IP adresu a PORT robota Jupiter.
  • Socket čaká na pripojenie odosielateľa a príjme veľkosť obrazových dát ako štvorbajtové číslo.
  • Potom príjme samotné obrazové dáta a uloží ich do určeného priečinka.
  • Obrázok sa spracuje pomocou funkcie "calsification()".
  • Výsledok klasifikácie sa odošle naspäť odosielateľovi ako odpoveď a spojenie sa zatvorí.

Komponent na klasifikáciu obrázku

Tento komponent sa nachádza na Raspberry Pi a má na starosti klasifikáciu obrázku a rozpoznanie typu oblečenia, ktoré sa nachádza na obrázku.

  model = 'clothes/clothes_model_edgetpu.tflite'
  labels_path = 'clothes/clothing_labels.txt'
  input = 'clothes/clothing_labels.txt'

Ako prvé si nastavíme cesty k nášmu modelu, súboru s popismi tried a vstupnému obrázku.


  labels = read_label_file(labels_path) if labels_path else {}

  interpreter = make_interpreter(*model.split('@'))
  interpreter.allocate_tensors()

Načítame si model a vytvoríme inštanciu interpretera.


  size = common.input_size(interpreter)
  image = Image.open(input).convert('RGB').resize(size, Image.ANTIALIAS)

  params = common.input_details(interpreter, 'quantization_parameters')
  scale = params['scales']
  zero_point = params['zero_points']
  mean = 128.0
  std = 128.0
  if abs(scale * std - 1) < 1e-5 and abs(mean - zero_point) < 1e-5:
    # Input data does not require preprocessing.
    common.set_input(interpreter, image)
  else:
    # Input data requires preprocessing
    normalized_input = (np.asarray(image) - mean) / (std * scale) + zero_point
    np.clip(normalized_input, 0, 255, out=normalized_input)
    common.set_input(interpreter, normalized_input.astype(np.uint8))

Prekonvertujeme obrázok do RGB formátu, zmenšíme na veľkosť požadovanú modelom a ak je to potrebné predspracujeme ho, aby bol v správnom formáte a škálovanej hodnote pre model.


  # Run inference
  for _ in range(5):
      interpreter.invoke()
      classes = classify.get_classes(interpreter, 1, 0.0)

  print('-------RESULT--------')
  for c in classes:
    print('%s: %.5f' % (labels.get(c.id, c.id), c.score))
  return (labels.get(c.id, c.id))

Spustíme inferenciu pomocou interpretera modelu a získame triedy a skóre klasifikácie. Na konci vrátime názov triedy rozpoznaného oblečenia.

Komponent na rozpoznanie osoby

Tento komponent slúži na to, aby podľa kusu oblečenia vedel povedať o akú konkrétnu osobu ide. Ak osobu ešte nepozná, vypýta si jej meno, inak ju pozdraví a osloví jej menom.

        self.people = {}
        self.recognized_clothing = ""
        rospy.init_node('people_recognizer')
        rospy.Subscriber('clothes', ClothesMessage, self.clothes_callback)
        
        while not rospy.is_shutdown():
            rospy.sleep(1)

Inicializujeme si slovník ľudí a ich oblečenia do ktorého budeme pridávať osoby, ktoré poznáme.

  • Vytvoríme uzol s názvom "people_recognizer".
  • Vytvoríme subscribera napojeného na topic "clothes" so správou typu ClothesMessage, ktorú sme si vytvorili a funkciou "self.clothes_callback()"

    def recognize_person(self):
        if not self.known_person():
            self.add_person()
        self.say_hello()

Táto metóda slúži na rozpoznanie osoby. Ak osoba nie je známa (metóda known_person vráti False), volá sa metóda "add_person()" na pridanie novej osoby. Potom sa zavolá metóda "say_hello()" na privítanie osoby.


    def clothes_callback(self, data):
        if data.glasses:
            self.recognized_clothing = "glasses"
        if data.cap:
            self.recognized_clothing = "cap"
        else:
            self.recognized_clothing = "red_t_shirt"

        self.recognize_person()

Metóda "clothes_callback()" je volaná pri príchode správy typu ClothesMessage na topic "clothes". Na základe informácií v správe sa nastaví hodnota recognized_clothing na základe oblečenia. Potom sa volá metóda "recognize_person()" na rozpoznanie osoby.


    def say_hello(self):
        person = self.people[self.recognized_clothing]
        talker = Talker(self.recognized_clothing)
        talker.talk()
        talker = Talker("Hello ", person)
        talker.talk()

Na základe typu oblečenia v správe sa zo slovníka vyberie meno osoby, ktorú má robot pozdraviť.


    def known_person(self):
        return self.recognized_clothing in self.people

    def add_person(self):
        talker = Talker("Please enter your name below:")
        talker.talk()
        name = input("YOUR NAME HERE:")
        self.people[self.recognized_clothing] = name
  • Metóda "known_person()", slúži na zistenie, či osobu už poznáme alebo ešte nie.
  • Ak osobu nepoznáme tak metóda "add_person()" osobu do slovníka pridá po vypýtaní mena z konzole s názvom daného oblečenia ako kľúčom.

Komunikácia jednotlivých komponentov

Tieto diagramy znázorňujú priebeh komunikácie medzi jednotlivými komponentami programu, ako aj medzi Jupiterom a Raspberry Pi.

top_camera

server_klient

people_recognizer

Význam jednotlivých častí diagramu je bližšie popísaný TU.