Модели обнаружения и распознавания ансамблей PaddleOCR. Для тестирования используются четыре изображения. Каждое изображение, проходящее через детектор, образует 26–33 ограничивающих прямоугольника (всего 118), которые затем передаются в функцию get_rotate_crop_image (которая вызывает cv2.warpPerspective) для подготовки входных данных для модели распознавания.
Проблема:
После изменения вызывающего кода в рамках поддержки пакетного вывода этот вызов cv2.warpPerspective включается. в среднем на порядок больше времени.
Расследование:
В последовательном случае:
- занимает 0,012678146362304688 секунд или 0,78% от общего времени
Код: Выделить всё
get_rotate_crop_image
- среднее время выполнения cv2.warpPerspective составляет 0,00028243048477172853 code> секунд
- занимает 0,4442577362060547 или 23,85% от общего времени
Код: Выделить всё
get_rotate_crop_image
- среднее время выполнения cv2.warpPerspective составляет 0,0037425978709075412 > секунд
С тех пор я внес изменения для запуска get_rotate_crop_image в пуле потоков; это действительно компенсирует ухудшение задержки, но среднее время выполнения cv2.warpPerspective остается в 10 раз больше, чем в последовательном случае, поэтому производительность остается под вопросом.
Код: Выделить всё
def process_image(index_bbox):
index, bbox = index_bbox
image = ori_im[index]
return get_rotate_crop_image(image, bbox)
with ThreadPoolExecutor() as executor:
img_crop_list = list(
executor.map(
process_image,
[
(index, box)
for index, batch in enumerate(dt_boxes)
for box in batch
],
)
)
Из-за различий в масштабировании, вызванных предварительной обработкой пакетов изображений, вызов cv2.warpPerspective может работать с изображениями большего размера и, следовательно, использовать больше памяти.
Однако после проверки входного изображения и точек массивы, они похожи; на самом деле изображение меньше в случае пакетного вывода:
Код: Выделить всё
In [1]: import numpy as np
...: from PIL import Image
...: seq_img = Image.open(“sequential/max_time_source.png”)
...: seq_points = np.load(“sequential/max_time_points.npy”)
...: batch_img = Image.open(“batch/max_time_source.png”)
...: batch_points = np.load(“batch/max_time_points.npy”)
...: print(
...: f”Sequential image shape: {seq_img.size}\n”
...: f”Batch image shape: {batch_img.size}\n”
...: f”Sequential points:\n{seq_points}\n”
...: f”Batch points:\n{batch_points}”
...: )
Sequential image shape: (1130, 842)
Batch image shape: (960, 768)
Sequential points:
[[ 979. 185.]
[1073. 188.]
[1072. 208.]
[ 978. 206.]]
Batch points:
[[832. 159.]
[911. 159.]
[911. 176.]
[832. 176.]]
Если интересно воспроизвести это, два набора входных данных были загружены на Google Диск.
Гипотеза №2
Память не освобождается после каждого из этих 118 вызовов get_rotate_crop_image/cv2.warpPerspective. Память, выделенная для ~30 вызовов на каждый вызов библиотеки PaddleOCR (последовательный случай), сравнительно меньше, чем память, используемая в пакетном случае. В последнем случае процессор перегружается, вызывая наблюдаемое замедление.
Я не очень разбираюсь в профилировании памяти, но я попробовал Memory_profiler и создал приведенные ниже диаграммы, которые подтверждают увеличение использование памяти в случае пакетной обработки...
Я могу ошибаться, но мне не кажется, что использование памяти ~930 МБ по сравнению с ~700 МБ позволит это большая разница, тем более, что машина, используемая для профилирования, имеет 16 ГБ памяти, а замедление наблюдается и на машинах с гораздо большим объемом памяти (128 ГБ).
Я не знаю, что делать на этом этапе.
Профиль последовательной памяти:
Профиль пакетной памяти:

Подробнее здесь: https://stackoverflow.com/questions/790 ... emory-leak