0

I have a project where I am displaying the live video feed from the USB camera using opencv. In between the code, there is a condition. When it becomes True I have to call an API to get some data which I am displaying on the live video feed frame. Calling the API and getting the response takes some time (2-3sec) during which the frame is freezed which doesn't look good. Below is the code snippet:

if config['ROIX1'] < f_startX and config['ROIX2']:
    # Call the API and get the response
    """
    OTHER CODE
    """

    conn = http.client.HTTPSConnection('api.tive.com')
    conn.request("POST", "/get_status", json_data, headers)
    response = conn.getresponse()
    rdata = response.read()
    rdata = rdata.decode('utf8')
    rdata = json.loads(rdata)

    subject = rdata['subject']

    txt = "SUBJECT: {}".format(subject)
    cv2.putText(frame, txt, (5, 30), cv2.FONT_HERSHEY_COMPLEX_SMALL, 1, (0, 0, 255), 1)

    cv2.imshow(win_name, frame)

    key = cv2.waitKey(1)
    if key == ord('q'):
        break

In above code when the if condition becomes True, code makes a call to the API and the response rdata takes some time due to which the application freezes for some time. How can I effectively handle calling and getting the response from the API in another thread and once it gets the result, it is shown on frame. I am not very experienced in threads. Please help. Thanks

S Andrew
  • 5,592
  • 27
  • 115
  • 237

2 Answers2

4

I Believe the kind of logic you're looking for is something like:

1) start thread for requesting api

2) while requesting is not finished do some other task ( show frame without api data)

3) break while when done, (show frame with api data)

I believe the key for your answer is using thread.is_alive, which checks whether a thread is alive and returns a boolean value based on the thread status. is_alive() method is often used in thread synchronisation.

I would do what you're looking for by doing something similar to this dummy code:

import sys
import trace
import threading
import time
def show_frame(subject=""):
    if subject:
        print(f"showing frame with {subject}")
    else:
        print(f"showing frame")

def request_api():
    time.sleep(5)
    return "data from api"

def show_frame_from_api():
    subject_dataAPI=request_api()
    show_frame(subject=subject_dataAPI)

t = threading.Thread(target=show_frame_from_api, args=())
t.start()

while t.is_alive():
    show_frame()

Applying it in your example, I would suggest something like this :

import sys
import trace
import threading
import time
def show_frame(win_name, frame, apiText=""):
    if apiText:
        text=f'display with {apiText}'
        cv2.putText(frame, text, (5, 30), cv2.FONT_HERSHEY_COMPLEX_SMALL, 1, (0, 0, 255), 1)
        cv2.imshow(win_name, frame)
    else
        text=f'display without api text :('
        cv2.putText(frame, text, (5, 30), cv2.FONT_HERSHEY_COMPLEX_SMALL, 1, (0, 0, 255), 1)
        cv2.imshow(win_name, frame)

def show_frame_from_api(win_name, frame):
    subject_dataAPI=request_api()
    show_frame(win_name, frame, apiText=subject_dataAPI)


def request_api():
    conn = http.client.HTTPSConnection('api.tive.com')
    conn.request("POST", "/get_status", json_data, headers)
    response = conn.getresponse()
    rdata = response.read()
    rdata = rdata.decode('utf8')
    rdata = json.loads(rdata)

    subject = rdata['subject']

    txt = "SUBJECT: {}".format(subject)
    return txt

if config['ROIX1'] < f_startX and config['ROIX2']:
    # Call the API and get the response
    """
    OTHER CODE
    """

    t = threading.Thread(target=show_frame_from_api, args=(win_name, frame))
    t.start()

    while t.is_alive():
        show_frame(win_name, frame)


    key = cv2.waitKey(1)
    if key == ord('q'):
        break
0

The threading library in Python is pretty easy to use and is well documented.

The first thing you need to do is to wrap the code that you want to run independently as a function, then you can use the library to run that function in a separate thread, as follows:

import threading

thread = threading.Thread(target=yourfunction, args=('arg1', 'arg2'))
thread.start()

Note that getting the result value from the executed function is a bit tricky, so I would suggest you to run the whole block (i.e the API call + displaying the frame) in a separate thread. Your code should look like:

def show_frame_from_api(win_name, frame):
    conn = http.client.HTTPSConnection('api.tive.com')
    conn.request("POST", "/get_status", json_data, headers)
    response = conn.getresponse()
    rdata = response.read()
    rdata = rdata.decode('utf8')
    rdata = json.loads(rdata)

    subject = rdata['subject']

    txt = "SUBJECT: {}".format(subject)
    cv2.putText(frame, txt, (5, 30), cv2.FONT_HERSHEY_COMPLEX_SMALL, 1, (0, 0, 255), 1)

    cv2.imshow(win_name, frame)

if config['ROIX1'] < f_startX and config['ROIX2']:
    # Call the API and get the response
    """
    OTHER CODE
    """

    t = threading.Thread(target=show_frame_from_api, args=(win_frame, frame))
    t.start()

    key = cv2.waitKey(1)
    if key == ord('q'):
        break

Whether you include the OTHER CODE block and the cv2.waitKey block inside the function depends on what are you trying to achive, but you should get the idea.

Note that you shouldn't call t.join() to wait for the thread to finish because that would freeze your main thread as well. As I already mentioned, the simplest approach is just to delegate to the thread all the action regarded to the API call and the processing of the response.

Martín De la Fuente
  • 6,155
  • 4
  • 27
  • 28