Try loading the image in a separate thread (OpenCV-Python)

Recently I found out about a library called trio.

[Python + Trio asynchronous coding pattern] (https://qiita.com/yura/items/689d065aba00fe14fbba)

Please refer to the.


If you write an image processing / image recognition program, you will want to load the next image during the image processing / recognition time. So I decided to write a multithreaded program in Python.

  1. Write a serialized version.
  2. Review the multithreaded framework in that language.
  3. Think about which process and which process are independent and how multithreading is possible.
  4. Implement the multithreaded version

Write a sequential version.

I wrote a sequential processing version as described later. I want to load the next image during this detection process. For operation, prepare an input image to detect people, pat = r"*.png " Please rewrite the part of.

Sequential processing version

detectLoop.py


# -*- coding: utf-8 -*-
# pylint: disable-msg=C0103
import glob
import cv2
hog = cv2.HOGDescriptor()
hog.setSVMDetector(cv2.HOGDescriptor_getDefaultPeopleDetector())

def detect(img):
    found = hog.detectMultiScale(img, winStride=(8, 8), padding=(32, 32), scale=1.05)
    return found

def draw_detections(img, rects, thickness=1):
    for x, y, w, h in rects:
        # the HOG detector returns slightly larger rectangles than the real objects.
        # so we slightly shrink the rectangles to get a nicer output.
        pad_w, pad_h = int(0.15*w), int(0.05*h)
        cv2.rectangle(img, (x+pad_w, y+pad_h), (x+w-pad_w, y+h-pad_h), (0, 255, 0), thickness)

if __name__ == '__main__':
    pat = r"*.png "
    names = glob.glob(pat)[:100]

    e1 = cv2.getTickCount()

    imgname = names[0]
    img = cv2.imread(imgname)

    for i in range(1, len(names), 1):
        found, scores = detect(img)
        imgnameNext = names[i]
        imgNext = cv2.imread(imgnameNext)
        if 0:
            print i, imgnameNext, imgNext.shape
            print found
        oldImg, imgname, img = img, imgnameNext, imgNext

        draw_detections(oldImg, found)
        cv2.imshow("detected", oldImg)
        cv2.waitKey(1)

    e2 = cv2.getTickCount()
    timeDiff = (e2 - e1)/ cv2.getTickFrequency()
    print timeDiff

Review the multithreaded framework in that language

"There are two ways to handle threads in the threading module. Create a subclass of threading.Thread. Create an instance of threading.Thread. " However, let's choose a method to create an instance directly.

First, let's look at a multithreaded example of processing that does not return a result from a function.

Then, it became clear that the next part was the main point.

\ # Create an instance of the thread. t = threading.Thread (target = function, args = tuple of function arguments) t.start() Other processing t.join()

In this example, the result of the function cannot be returned, so in order to return the result between threads, Queue is used to pass data between threads.

def function name(queue,argument):
Function processing
    queue.put(Return value)

t = threading.Thread(target=Function name, args=(queue,argument)) 
t.start()
#Write the process
t.join()
Function return value= queue.get()

Think about how multithreading is possible

There are two ways to consider whether to use a separate thread for image capture or person detection processing. Here, I tried to make the person detection process a separate thread. By writing like this, loading the next image and processing the loaded image It seems that it can be operated in parallel.

Implement a multithreaded version

Multithreaded version

detectLoop_thread.py


# -*- coding: utf-8 -*-
# pylint: disable-msg=C0103
import threading
import Queue
import glob
import cv2

global hog
hog = cv2.HOGDescriptor()
hog.setSVMDetector(cv2.HOGDescriptor_getDefaultPeopleDetector())

def detect2(queue, img):
    global hog
    found = hog.detectMultiScale(img, winStride=(8, 8), padding=(32, 32), scale=1.05)
    queue.put(found)

def draw_detections(img, rects, thickness=1):
    for x, y, w, h in rects:
        # the HOG detector returns slightly larger rectangles than the real objects.
        # so we slightly shrink the rectangles to get a nicer output.
        pad_w, pad_h = int(0.15*w), int(0.05*h)
        cv2.rectangle(img, (x+pad_w, y+pad_h), (x+w-pad_w, y+h-pad_h), (0, 255, 0), thickness)

if __name__ == '__main__':
    pat = r"*.png "
    names = glob.glob(pat)[:100]

    e1 = cv2.getTickCount()
    queue = Queue.Queue()
    imgname = names[0]
    img = cv2.imread(imgname)

    for i in range(1, len(names), 1):
        t = threading.Thread(target=detect2, args=(queue, img, ))
        t.start()
        imgnameNext = names[i]
        imgNext = cv2.imread(imgnameNext)
        t.join()
        found, scores = queue.get()
        if 0:
            print i, imgnameNext, imgNext.shape
            print found
        oldImg, imgname, img = img, imgNext, imgNext
        draw_detections(oldImg, found)
        cv2.imshow("detected", oldImg)
        cv2.waitKey(1)

    e2 = cv2.getTickCount()
    timeDiff = (e2 - e1)/ cv2.getTickFrequency()
    print timeDiff

As for the operation result, the processing time is reduced by about 0.8 seconds when reading 100 png files. (In the example, it can be seen that it is necessary to reduce the time required for the person detection process itself.)

Sequential processing version seconds 29.5429292224 Multithreaded version of seconds 28.7976980869

# Multi-threaded version (another version)

detectLoop_thread2.py


# -*- coding: utf-8 -*-
# pylint: disable-msg=C0103
import threading
import Queue
import glob
import cv2

global hog
hog = cv2.HOGDescriptor()
hog.setSVMDetector(cv2.HOGDescriptor_getDefaultPeopleDetector())

def imread2(queue, imgnameNext):
    imgNext = cv2.imread(imgnameNext)
    queue.put(imgNext)

def draw_detections(img, rects, thickness=1):
    for x, y, w, h in rects:
        # the HOG detector returns slightly larger rectangles than the real objects.
        # so we slightly shrink the rectangles to get a nicer output.
        pad_w, pad_h = int(0.15*w), int(0.05*h)
        cv2.rectangle(img, (x+pad_w, y+pad_h), (x+w-pad_w, y+h-pad_h), (0, 255, 0), thickness)

if __name__ == '__main__':
    pat = r"*.png "
    names = glob.glob(pat)[:100]

    e1 = cv2.getTickCount()
    queue = Queue.Queue()
    imgname = names[0]
    img = cv2.imread(imgname)

    for i in range(1, len(names), 1):
        imgnameNext = names[i]
        t = threading.Thread(target=imread2, args=(queue, imgnameNext, ))
        t.start()
        found, scores = hog.detectMultiScale(img, winStride=(8, 8), padding=(32, 32), scale=1.05)
        t.join()
        imgNext = queue.get()
        
        if 0:
            print i, imgnameNext, imgNext.shape
            print found
        oldImg, imgname, img = img, imgNext, imgNext
        draw_detections(oldImg, found)
        cv2.imshow("detected", oldImg)
        cv2.waitKey(1)

    e2 = cv2.getTickCount()
    timeDiff = (e2 - e1)/ cv2.getTickFrequency()
    print timeDiff

Postscript: What I thought recently. There is a possibility that the image is loaded and detected in each thread. If this method can be used, it should be possible to parallelize images with evenly distributed work rather than parallelizing them with completely different calculation times for image loading and detection.

http://docs.python.jp/2/library/multiprocessing.html

Referenced script example

16.2. threading — High level threading interface http://docs.python.jp/2/library/threading.html

Try a Python thread (2) http://bty.sakura.ne.jp/wp/archives/69

Multithreaded processing in Python http://qiita.com/konnyakmannan/items/2f0e3f00137db10f56a7

Related article Try loading images in a separate thread (C ++ version) http://qiita.com/nonbiri15/items/016bb38eb42a219e98e2


Note: I heard that there is a library called TRIO and you should use it.

https://trio.readthedocs.io/en/latest/#

Recommended Posts

Try loading the image in a separate thread (OpenCV-Python)
Cut out A4 print in the image
The image is a slug
Try a similar search for Image Search using the Python SDK [Search]
I wrote a script that splits the image in two
Try throwing a query in Redash
Detect mosaic points in the image
Handle requests in a separate process
Try to edit a new image using the trained StyleGAN2 model
Cython to try in the shortest
Try blurring the image with opencv2
Try using the Wunderlist API in Python
Try using the Kraken API in Python
Try using the HL band in order
Write the test in a python docstring
Try drawing a simple animation in Python
Change the list in a for statement
Run the Python interpreter in a script
Try the new scheduler chaining in PyTorch 1.4
Try hitting the YouTube API in Python
Try a functional programming pipe in Python
Try hitting the Spotify API in Django.
Arrange the numbers in a spiral shape
Create a Python image in Django without a dummy image file and test the image upload
Try adding fisheye lens distortion to the image
[Python] Get the files in a folder with Python
Try to calculate a statistical problem in Python
Use the latest pip in a virtualenv environment
[Cloudian # 7] Try deleting the bucket in Python (boto3)
Get the caller of a function in Python
Try using the BitFlyer Ligntning API in Python
Make a copy of the list in Python
Find the number of days in a month
A program that searches for the same image
Get only the subclass elements in a list
Determining if there are birds in the image
Determine the numbers in the image taken with the webcam
Detect folders with the same image in ImageHash
Set a fixed IP in the Linux environment
Try modifying the TortoiseHg file view a bit
Output in the form of a python array
[Python] Find the transposed matrix in a comprehension
Try implementing the Monte Carlo method in Python
A supplement to "Camera Calibration" in OpenCV-Python Tutorials
Isn't there a default value in the dictionary?
Try using the DropBox Core API in Python
Try to make a blackjack strategy by reinforcement learning (② Register the environment in gym)
A memo that implements the job of loading a GCS file into BigQuery in Python