Я создал программу Tkinter, которая отображает изображения, обработанные с помощью OpenCV. Графический интерфейс Tkinter имеет кнопки для выбора папки, просмотра следующего изображения и просмотра предыдущего изображения, а также холст, на котором отображаются эти изображения. И когда человек с помощью переключателя проверяет, какой тип изображения это изображение, запись сохраняется.
Вот как это выглядит для пользователей:
В течение 1 секунды, пока человек 5 принимает решение, я хочу начать обработку и подготовку следующего изображения 3, а затем немедленно отобразить его, когда я нажму кнопку. Я думаю, что это значительно улучшит пользовательский опыт.
Как я могу это сделать? Я был бы признателен, если бы вы могли сказать мне хотя бы одно ключевое слово о том, какие технологии мне следует изучить.
Я изменил программу на основе ответа Ахмеда АЕКа. Это сработало хорошо и, очевидно, немного улучшило пользовательский опыт, заранее подготовив следующее изображение. Однако часть Canvas.draw(), рисующая заранее подготовленное изображение в Tkinter, сама по себе отнимает время. Так что я не смог далеко продвинуться в улучшении его до уровня, на котором нет абсолютно никаких задержек. Это моя вина, что я заранее не досконально проанализировал время на каждый шаг.
Я не мог попробовать Redis, потому что существовал первоначальный входной барьер. Спасибо вам обоим за отличные ответы.
вам следует использовать многопоточность, т. е. переносить работу на другой поток, чтобы основной поток не блокировался, наиболее надежным способом является использование Threadpool следующим образом.
from multiprocessing.pool import ThreadPool
class ImageRepository:
def __init__(self):
self.next_image_future = None
self.threadpool = ThreadPool(1) # 1 worker, we don't need more
def start_image_processing(self):
# initialize and start the first task
self.next_image_future = self.threadpool.apply_async(self.load_next_image)
def get_next_image(self):
if self.next_image_future is not None:
# get the image
new_image = self.next_image_future.get()
# submit task for the next one in future
self.next_image_future = \
self.threadpool.apply_async(self.load_next_image)
return new_image
def load_next_image(self):
print("loading and processing image")
return 5 # new image object
repo = ImageRepository()
repo.start_image_processing() # start grabbing the first image
# get next image and initiate work for the one after it
new_image = repo.get_next_image()
и поэтому каждый раз, когда вы получаете изображение, вы будете назначать задачу другому потоку для загрузки и обработки следующего.
Я думаю, вы могли бы создать очень элегантное решение, используя Redis, который представляет собой молниеносный сервер со структурой данных в памяти, идеально подходящий для кэширования и предварительной выборки. У него есть клиенты для C/C++, Python, Ruby, bash
, Java и так далее. В вашем случае я бы использовал его для хранения двух структур данных:
Основная идея состоит в том, чтобы позволить приложению Tk помещать список имен изображений в рабочую очередь при запуске приложения и, например, всякий раз, когда пользователь меняет каталог. А затем забирайте обработанное изображение из Redis всякий раз, когда ему нужно его отобразить.
Затем вы запускаете рабочий процесс, который просто ожидает появления имен изображений в очереди, обрабатывает их и сохраняет результат в быстром кэше Redis, готовом для использования приложением Tk.
Это имеет некоторые преимущества:
Таким образом, код рабочего процесса обработки изображений будет таким, и вы можете просто запустить несколько одинаковых рабочих процессов, если хотите более высокую пропускную способность:
connect to Redis
while True:
# Wait for image, popping next from queue
image = BRPOP from work queue (implemented as Redis LIST)
... do processing with OpenCV
# Store processed result as Redis STRING with expiry in 10 minutes
SET imagename processedImage EX 600
БРПОП описан здесь.
СЕТ описан здесь.
Код внутри Tk для помещения одного или нескольких имен файлов в очередь заданий будет выглядеть следующим образом:
connect to Redis
LPUSH one or more filenames to work queue in Redis
LPUSH описан здесь.
А если вы хотите отобразить обработанное изображение, проверьте его существование в Redis и загрузите его, если оно существует. Если его не существует в Redis, повторите запрос — возможно, с помощью RPUSH, чтобы он получил более высокий приоритет, или обработайте его самостоятельно и сохраните результат в Redis.
GET processed image
if image is NULL:
re-request as above
wait till ready
GET описан здесь.
Поскольку Redis имеет клиент bash
(т. е. командной строки), он также упрощает отладку. Вы можете проверить длину рабочей очереди с вашего терминала. Вы можете помещать элементы в рабочую очередь и удалять элементы из рабочей очереди. Вы можете увидеть, было ли обработано изображение и т. д., находясь вне приложения, без необходимости его изменения.
Предположим, вы вызываете свою рабочую очередь WQUEUE
и хотите узнать ее длину, просто используя терминал:
redis-cli LLEN WQUEUE
Или вы хотите перечислить все элементы в очереди:
redis-cli LRANGE WQUEUE 0 -1
Или вы хотите поместить имя файла изображения londonbridge.jpg
в рабочую очередь:
redis-cli LPUSH WQUEUE londonbridge.jpg
Или вы хотите узнать, было ли обработано изображение londonbridge.jpg
, и если да, то извлечь его в файл:
redis-cli GET londonbridge.jpg > processedImage.jpg
Или вы хотите увидеть названия всех обработанных изображений:
redis-cli KEYS '*'
Обратите внимание, что Redis подключен к сети, поэтому вы можете запустить Redis на одном компьютере и подключиться к нему с любого другого компьютера, указав его имя хоста или IP-адрес:
# Get length of work queue on host 192.168.0.10
redis-cli -h 192.168.0.10 LLEN WQUEUE
Я думаю, вам захочется использовать «предварительную загрузку» в Google или что-то подобное.