236 lines
11 KiB
Python
236 lines
11 KiB
Python
|
# Creation Date: 14.01.2022
|
||
|
# Author: Kenan Gömek
|
||
|
# This script takes pictures with Picameras VideoPort like it will be used to work with OpenCV and saves it with OpenCV to have the real use case pictures.
|
||
|
# This script is designed for aligning the LED-Stripe horizontally to the image sensor with LEDs off
|
||
|
# Use:
|
||
|
# 1. turn on the LEDs to e.g. 005-000-000 manually via bash or similar
|
||
|
# 2. execute this script
|
||
|
# 3. adjust shutter speed if desired with x,c,b,n,m (program starts with max shutter speed for selected frame rate)
|
||
|
# 4. align LED strip e.g. to the upper blue line and first led to the left edge of the image
|
||
|
# 5. take image of alignment with 'i'
|
||
|
# 6. end program with 'q'
|
||
|
|
||
|
import cv2 as cv
|
||
|
import picamera
|
||
|
from picamera.array import PiRGBArray
|
||
|
from fractions import Fraction
|
||
|
|
||
|
import time
|
||
|
from datetime import datetime
|
||
|
import os
|
||
|
|
||
|
import numpy as np
|
||
|
|
||
|
|
||
|
|
||
|
# Define camera settings
|
||
|
|
||
|
# divide origin resoluton by a number, to have the origin aspect ratio
|
||
|
|
||
|
# RESOLUTION = (3280, 2464) # Max Photo-Resolution CAM03 and CAM04 # no image with PiCamera Videoport at this Resolution.. Probably GPU Memory and CPU issues.
|
||
|
# RESOLUTION = (1640,1232) # 2nd best Resolution for CAM03 and CAM04 with FUll FOV (2x2 binning) # Mode 4
|
||
|
SENSOR_MODE = 4 # corresponding sensor mode to resolution
|
||
|
OUTPUT_RESOLUTION = (416, 320) # (1640x1232)/4=(410,308)
|
||
|
# (410,308) is being upscaled to (416,320) from ISP (warning in bash), but image will have still (410,308) pixels.
|
||
|
# OUTPUT_RESOLUTION = (820, 616) # (1640x1232)/2=(820,616)
|
||
|
# bash: frame size rounded up from 820x616 to 832x624
|
||
|
|
||
|
|
||
|
AWB_MODE = 'off' # Auto white balance mode
|
||
|
AWB_GAINS = (Fraction(485, 256), Fraction(397, 256)) # White Balance Gains to have colours read correctly: (red, blue)
|
||
|
ISO = 100 # ISO value
|
||
|
EXPOSURE_MODE = 'off'
|
||
|
FRAMERATE = 10 # frames per second. 40 fps is max for sensor mode 4
|
||
|
|
||
|
SLEEP_TIME = 2 # Time for sleep-mode for the camera in seconds. My default: 2 s
|
||
|
|
||
|
# Define Funcions
|
||
|
def take_image_picamera_opencv(shutter_speed):
|
||
|
|
||
|
# Initialise Camera
|
||
|
with picamera.PiCamera() as camera:
|
||
|
with PiRGBArray(camera) as output:
|
||
|
# Set camera settings
|
||
|
camera.sensor_mode = SENSOR_MODE # force camera into desired sensor mode
|
||
|
camera.resolution = OUTPUT_RESOLUTION # frame will be resized from GPU to this resolution. No CPU usage!
|
||
|
camera.framerate = FRAMERATE
|
||
|
|
||
|
camera.awb_mode = AWB_MODE
|
||
|
camera.awb_gains = AWB_GAINS
|
||
|
|
||
|
camera.iso = ISO
|
||
|
camera.shutter_speed = shutter_speed
|
||
|
camera.exposure_mode = EXPOSURE_MODE
|
||
|
|
||
|
time.sleep(SLEEP_TIME) # Camera warm-up time to apply settings
|
||
|
|
||
|
# camera.start_preview() # show camera preview through PiCamera interface
|
||
|
# camera.annotate_frame_num=True # Controls whether the current frame number is drawn as an annotation.
|
||
|
|
||
|
for frameidx, frame in enumerate(camera.capture_continuous(output, format='bgr', use_video_port=True)):
|
||
|
framenumber = frameidx+1 # frameidx starts with 0, framenumber with 1
|
||
|
image = frame.array # raw NumPy array without JPEG encoding
|
||
|
|
||
|
camera_exposure_speed = camera.exposure_speed # Retrieves the current shutter speed of the camera.
|
||
|
|
||
|
# cv.imshow("Current Frame", image) # display the image without text
|
||
|
img = alignment_procedure(image, shutter_speed, framenumber, camera_exposure_speed) # show the frame
|
||
|
|
||
|
framenumber_to_save = 7000 # frame 15 (no particular reason for frame 15)
|
||
|
trigger_save_frame = 'no' # trigger for saving the frame
|
||
|
if framenumber == framenumber_to_save and trigger_save_frame=='yes': # save desired frame
|
||
|
now = datetime.now(); d1 = now.strftime("%Y-%m-%d %H-%M-%S.%f")[:-3]
|
||
|
print(f"Take picture! framenumber: {framenumber} shutter speed: {shutter_speed} µs")
|
||
|
cv.imwrite(f"{path_saveFolder}/ss{shutter_speed}_Date {d1}.png", image)
|
||
|
break # break from the loop, because we took the image we wanted
|
||
|
|
||
|
output.truncate(0) # clear the stream for next frame
|
||
|
|
||
|
# Only uncomment following code if you display the image. No errors if not commented, but higher fps if commented.
|
||
|
# if q is pressed, break from loop.
|
||
|
pressed_key = cv.waitKey(1) & 0xff
|
||
|
if pressed_key == ord('q'):
|
||
|
break
|
||
|
elif pressed_key == ord('i'): # Take image from manipulated image if i is pressed
|
||
|
now = datetime.now(); d1 = now.strftime("%Y-%m-%d %H-%M-%S.%f")[:-3]
|
||
|
cv.imwrite(f"{path_saveFolder}/Date {d1}.png", img)
|
||
|
print('took image!')
|
||
|
elif pressed_key == ord('b'): # increase shutterspeed by 50
|
||
|
shutter_speed = round(shutter_speed+50)
|
||
|
camera.shutter_speed = shutter_speed
|
||
|
elif pressed_key == ord('n'): # increase shutterspeed by 500
|
||
|
shutter_speed = round(shutter_speed+500)
|
||
|
elif pressed_key == ord('m'): # max shutterspeed
|
||
|
shutter_speed = round(1/FRAMERATE*1e6)
|
||
|
camera.shutter_speed = shutter_speed
|
||
|
elif pressed_key == ord('x'): # decrease shutterspeed by 500
|
||
|
shutter_speed = round(shutter_speed-500)
|
||
|
camera.shutter_speed = shutter_speed
|
||
|
elif pressed_key == ord('c'): # decrease shutterspeed by 50
|
||
|
shutter_speed = round(shutter_speed-50)
|
||
|
camera.shutter_speed = shutter_speed
|
||
|
elif pressed_key == ord('o'): # set shutter speed to 0
|
||
|
shutter_speed = 0
|
||
|
camera.shutter_speed = shutter_speed
|
||
|
|
||
|
|
||
|
def display_image_with_text(img, shutter_speed, framenumber, camera_exposure_speed):
|
||
|
img = img.copy() # make copy of image and do not modify the original image
|
||
|
|
||
|
font = cv.FONT_HERSHEY_SIMPLEX # font
|
||
|
fontScale = 1 # fontScale
|
||
|
color = (255, 255, 255) # Font colour in BGR
|
||
|
thickness = 1 # Line thickness in px
|
||
|
|
||
|
# set text position
|
||
|
frame_width = int(img.shape[1])
|
||
|
frame_height = int(img.shape[0])
|
||
|
text_start_position_Y = int(round(frame_height*0.12)) # start position of text in pixels 12 % of frame height
|
||
|
text_linespacing = 50 # line spacing between two strings in pixels
|
||
|
# text_start_position_X = int(frame_width/4) # start text from 1/4 of image width
|
||
|
text_start_position_X = int(0) # start text from left edge of image
|
||
|
|
||
|
# set position in (x,y)-coordinated from top left corner. Bottom-left corner of the text string in the image.
|
||
|
pos_1 = (text_start_position_X, text_start_position_Y) # start text from 1/4 of image width
|
||
|
pos_2 = (text_start_position_X, text_start_position_Y+text_linespacing) # start text from 1/4 of image width
|
||
|
pos_3 = (text_start_position_X, text_start_position_Y+2*text_linespacing) # start text from 1/4 of image width
|
||
|
|
||
|
# define text to display
|
||
|
text_line_1 = f"set shttr-spd: {shutter_speed} us"
|
||
|
text_line_3 = f"Frame: {framenumber}"
|
||
|
text_line_2 = f"ret exp-spd: {camera_exposure_speed} us"
|
||
|
|
||
|
# put the text into the image
|
||
|
image_text_1 = cv.putText(img, text_line_1, pos_1, font,
|
||
|
fontScale, color, thickness, cv.LINE_AA)
|
||
|
image_text_2 = cv.putText(img, text_line_2, pos_2, font,
|
||
|
fontScale, color, thickness, cv.LINE_AA)
|
||
|
image_text_3 = cv.putText(img, text_line_3, pos_3, font,
|
||
|
fontScale, color, thickness, cv.LINE_AA)
|
||
|
|
||
|
cv.imshow("Current Frame", img) # display the image
|
||
|
|
||
|
def alignment_procedure(img, shutter_speed, framenumber, camera_exposure_speed):
|
||
|
img = img.copy() # make copy of image and do not modify the original image
|
||
|
|
||
|
font = cv.FONT_HERSHEY_SIMPLEX # font
|
||
|
fontScale = 1 # fontScale
|
||
|
color = (255, 255, 255) # Font colour in BGR
|
||
|
thickness = 1 # Line thickness in px
|
||
|
|
||
|
# set text position
|
||
|
frame_width = int(img.shape[1])
|
||
|
frame_height = int(img.shape[0])
|
||
|
text_start_position_Y = int(round(frame_height*0.12)) # start position of text in pixels 12 % of frame height
|
||
|
text_linespacing = 50 # line spacing between two strings in pixels
|
||
|
# text_start_position_X = int(frame_width/4) # start text from 1/4 of image width
|
||
|
text_start_position_X = int(0) # start text from left edge of image
|
||
|
|
||
|
# set position in (x,y)-coordinated from top left corner. Bottom-left corner of the text string in the image.
|
||
|
pos_1 = (text_start_position_X, text_start_position_Y) # start text from 1/4 of image width
|
||
|
pos_2 = (text_start_position_X, text_start_position_Y+text_linespacing) # start text from 1/4 of image width
|
||
|
pos_3 = (text_start_position_X, text_start_position_Y+2*text_linespacing) # start text from 1/4 of image width
|
||
|
|
||
|
# define text to display
|
||
|
text_line_1 = f"set shttr-spd: {shutter_speed} us"
|
||
|
text_line_3 = f"Frame: {framenumber}"
|
||
|
text_line_2 = f"ret exp-spd: {camera_exposure_speed} us"
|
||
|
|
||
|
# put the text into the image
|
||
|
image_text_1 = cv.putText(img, text_line_1, pos_1, font,
|
||
|
fontScale, color, thickness, cv.LINE_AA)
|
||
|
image_text_2 = cv.putText(img, text_line_2, pos_2, font,
|
||
|
fontScale, color, thickness, cv.LINE_AA)
|
||
|
image_text_3 = cv.putText(img, text_line_3, pos_3, font,
|
||
|
fontScale, color, thickness, cv.LINE_AA)
|
||
|
|
||
|
# draw lines into image
|
||
|
alignment_line_thickness = 1 # thickness of the alignment line in pixels
|
||
|
alignment_line_offset = 21 # offset of the alignment line from the center
|
||
|
frame_center = round(frame_height/2) # center of the frame
|
||
|
offset_center = frame_center + 70 # offset of the center for e.g. have the alignment lines more in the bottom part of the image
|
||
|
alignment_row_start = offset_center-alignment_line_offset
|
||
|
alignment_row_end = offset_center+alignment_line_offset
|
||
|
|
||
|
img[offset_center, :, :] = [255,255,255] # bgr format
|
||
|
img[alignment_row_start-alignment_line_thickness:alignment_row_start, :, :] = [255,0,0] # bgr format
|
||
|
img[alignment_row_end:alignment_row_end+alignment_line_thickness, :, :] = [0,0,255] # bgr format
|
||
|
|
||
|
cv.imshow("Current Frame", img) # display the image
|
||
|
return img
|
||
|
|
||
|
|
||
|
# Start Script
|
||
|
|
||
|
# Create folder for saving the captured pictures
|
||
|
now = datetime.now(); d1 = now.strftime("%Y-%m-%d %H-%M")
|
||
|
path_cwd = os.getcwd()
|
||
|
|
||
|
path_saveFolder = path_cwd+r"/Ledstripe_alignment_"+d1
|
||
|
try:
|
||
|
os.mkdir(path_saveFolder)
|
||
|
except OSError:
|
||
|
print("Error! Ending script. Try it again in 1 minute")
|
||
|
quit()
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
# start capture series for different shutter speeds
|
||
|
print('start caputure series...')
|
||
|
|
||
|
# take_image_picamera_opencv(shutter_speed=2000000) #Can see something at normal light conditions
|
||
|
take_image_picamera_opencv(shutter_speed=round(1/FRAMERATE*1e6)) # max shutter-speed depending on fps: 1/2 fps = 500 000 µs
|
||
|
|
||
|
# End Script
|
||
|
|
||
|
cv.destroyAllWindows()
|
||
|
|
||
|
print('Script finished')
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|