Размышляя над прикладной задачей «как прочитать мерцающую строку с номером электробуса» с помощью обычной камеры можно прийти к совершенно неожиданным результатам. Например, получить футуристические снимки окружающего ландшафта и вещей. Решение данной задачи с помощью python, а также несколько инопланетных изображений далее в статье.
Короткий ролик демонстрирует, как выглядит номер электробуса при съемке с помощью камеры raspberry pi:
ссылка на rutube
Заметно, что присутствует мерцание. Данный эффект не позволяет сделать качественный снимок, на котором будет присутствовать номер транспортного средства. На снимке будет либо видна часть номера, либо он вовсе будет отсутствовать. Как повезет.
Как прочитать номер для дальнейшего распознавания? Первое, что приходит на ум — это попытаться рассинхронизировать камеру с частотой обновления изображения на номерном знаке электробуса. Но что, если эта частота у каждого транспортного средства своя. Да и как это сделать на примере с picamera (предполагается использование одноплатника raspberry pi)?
Оказывается, есть еще один вариант решения задачи. Данный подход, как выяснилось, активно используется в астрономии при «фотографировании» звезд. Чтобы изображение звезды получилось качественнее, в некоторых случаях производится «сложение» снимков.
Продемонстрируем данный эффект на базе raspberry pi и python:
import time
import picamera
import cv2,os
import numpy as np
import glob
from datetime import datetime
frames = 3
def filenames():
frame = 0
while frame < frames:
yield 'photos/image%02d.jpg' % frame
frame += 1
with picamera.PiCamera(resolution='720p', framerate=30) as camera:
camera.start_preview(fullscreen=False, window = (100, 20, 640, 480))
#camera.vflip = True
camera.rotation = 270
# Give the camera some warm-up time
time.sleep(2)
start = time.time()
camera.capture_sequence(filenames(), use_video_port=True)
finish = time.time()
print('Captured %d frames at %.2ffps' % (
frames,
frames / (finish - start)))
os.chdir('photos/')
files=[]
for file in glob.glob('*.jpg'):
#print(file)
files.append(file)
x=0
def glue(img1,img2):
return cv2.addWeighted(img1, 0.5, img2, 0.5, 0)
for f in range(len(files)-1):
img1 = cv2.imread(files[x])
img2 = cv2.imread(files[x+1])
dst = glue(img1,img2)
dst+=dst
x+=1
cv2.imwrite(f'{str(datetime.now()).split(" ")[1]}.png',dst)
#cv2.imshow('Blended Image',dst)
#cv2.waitKey(0)
#cv2.destroyAllWindows()
for file in glob.glob('image*.jpg'):
os.remove(file)
Как несложно догадаться, основная «магия» происходит в функции glue, объединяющей снимки в нечто новое.
С помощью такого нехитрого подхода можно получать совершенно необычные снимки:
Но и в том числе решить прикладную задачу:
p.s. Возможно, классические методы cv не будут открытием для кого-то. Однако, об их (методах) существовании порой забываешь и начинаешь закапываться в дебри реализации.
Комментарии (18)
xpbim3_xpbim3
18.09.2024 09:38+5Странно что цветовые каналы выщелкнуло в переполнение.
Вообще говоря cv тут нафик не нужна, это и в контексте numpy отлично сработает:
result = (img1*0.5 + img2*0.5)
result_clipped = np.clip(result, 0, 255).astype(np.uint8)
Причем я бы кастанул в грейскейл и это сработает даже со стэком n изображений в тензорной форме, например положим что img_stack.shape == (n, 1, y, x) типа np.uint8
то можно дать:
result = np.sum(img_stack.astype(int), axis = 0) / n
SquareRootOfZero
18.09.2024 09:38+2Странно что цветовые каналы выщелкнуло в переполнение.
Я, возможно, чего-то не догоняю, конкретно, что автор делает вот в этих вот двух строчках:
dst = glue(img1,img2) dst+=dst
Сперва в dst кладётся среднее арифметическое двух соседних по времени кадров (ОК), потом содержимое dst удваивается путём сложения самого с собой - видимо, тут и возникает переполнение, ибо как ему тут не возникнуть. Затем цикл уходит на следующую итерацию и значение dst затирается средним двух следующих кадров? Т. е., после завершения цикла пишется удвоенное среднее двух последних, а все предыдущие итерации были мартышкиным трудом?
zoldaten Автор
18.09.2024 09:38@xpbim3_xpbim3
| Вообще говоря cv тут нафик не нужна, это и в контексте numpy отлично сработает:не нужна, если просто сложением заниматься и не делать снимки с камеры. но их приходится делать.
да и результат, не отличается от того, что предложил SquareRootOfZero -
Zara6502
18.09.2024 09:38+1мне кажется можно воспользоваться контролем времени затухания "люминофора"
которого нетто есть например второй кадр суммируется с первым, у которого понижена яркость например на 25%, третий из 1-2-3 кадров так же -25% и -50%. Таким образом яркие участки будут уходить в белое, а "моргающие" точки в серое.
DimPal
18.09.2024 09:38+3А если не суммировать, а взять максимум яркости от двух соседних кадров?
zoldaten Автор
18.09.2024 09:38каким образом ?
DimPal
18.09.2024 09:38Методом попиксельного сложения соседних кадров (с арифметикой насыщения). Если удасться сделать с компенсацией движения, то ещё лучше. Хотя в сферическом случае через-кадрового пропадания светящейся надписи и без компенсации станет лучше.
zoldaten Автор
18.09.2024 09:38я не совсем вас понимаю. в коде можете продемонстрировать ?
DimPal
18.09.2024 09:38Извиняюсь - чего то меня заклилнило, не то написал. Не "попиксельное сложение", а попиксельный максимум из двух соседних кадров.
SquareRootOfZero
18.09.2024 09:38Попиксельный максимум двух соседних кадров будет в коде как-то так:
import cv2 # two consecutive frames as grayscale img1 = cv2.imread("frame1.jpg", cv2.IMREAD_GRAYSCALE) img2 = cv2.imread("frame2.jpg", cv2.IMREAD_GRAYSCALE) cond = img2 > img1 # condition where img2 pixel value is greater img1[cond] = img2[cond] # replace img1 pixel with img2 pixel cv2.imwrite("maxvalue.jpg", img1)
Проверить эффект я не смог - не на чем, в видео каждый второй кадр содержит весь номер целиком и ярко, ну, выбрал максимум, ну и чо. Плюс кадры часто сильно смещаются друг относительно друга, а решение проблемы стабилизации автором поста не заявлено. Но на стабильном мерцающем наборе чо б ему не работать. Если цвет цифр всегда один и тот же, можно ещё вместо яркости попробовать брать максимум по hue, наверное.
zoldaten Автор
18.09.2024 09:38взял 2 снимка, идущие подряд. т.е. даже не через кадр и не из середины видеоряда:
получилось следующее:
это будет сложнее распознать. а так, идея хорошая.
zoldaten Автор
18.09.2024 09:38p.s.
с '3' в конкретно данном случае тоже все неоднозначно, но остальные символы будут прочитаны ocr.
SquareRootOfZero
18.09.2024 09:38Два вопроса:
Идея со средневзвешенной яркостью понятна, но какую задачу автор решает, складывая получившуюся картинку саму с собой, по-сути, удваивая яркость (и, за счёт переполнения цветовых каналов, собственно, и получая свои "необычные" снимки)?
Чоза дичь происходит в финальном цикле?
Если распатронить ролик с ютуба на кадры, то там как минимум на каждом втором кадре номер автобуса присутствует целиком, ярко и чётко - это результат сжатия видео или изначально камера так сняла? А то, может, и огород городить не стоило?
zoldaten Автор
18.09.2024 09:38При сложении снимков получается снимок, на котором возможно гарантированно детектировать box с номером.
Про дичь: слипаются в безумном танце 3 кадра.
Если "разобрать" видео на кадры, действительно на одном из кадров можно "поймать" box, где более-менее отчетливо виден номер. Однако, для этого нужно: записать видео, разобрать видео на кадры, проанализировать несколько кадров, отбраковывая плохие. Это дорого для одноплатника. Кроме того, это не будет работать, если мерцающая строка длинная, т.е. нужен не только номер, но и надписи и т.п.
SquareRootOfZero
18.09.2024 09:38В смысле, удвоить яркость без учёта переполнения - это так и задумано?
Там не три кадра, там первый-второй, потом результат затирается на второй-третий.
Зачем писать видео, если можно просто сделать быстро несколько кадров подряд (вы ж их и так делаете) и выбрать лучший?
engine9
Похожая техника, очень необычный результат.
Radisto
Говорят, лягушки так видят