442 lines
20 KiB
Python
442 lines
20 KiB
Python
# Creation Date: 10.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.
|
||
# Update: This script is designed to take image series with different camera parameters
|
||
# Update-Comment:
|
||
# To-DO:
|
||
|
||
import serial
|
||
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
|
||
|
||
# Comment for Measurement-Log:
|
||
CAM_NR = "04" # CAM:01,02,03,04
|
||
|
||
COMMENT = ""
|
||
|
||
# Set Parameters for series with variable shutterspeeds
|
||
# Uncomment/Comment needed code parts for shutter speed lists:
|
||
#START_SHUTTER_SPEED = 50 # microseconds. 0=auto
|
||
#END_SHUTTER_SPEED = 100 # microseconds
|
||
#STEP_SHUTTER_SPEED = 25 # microseconds
|
||
# shutter_speeds_add = [47, 66, 85, 104] # microseconds. shutter speeds to additionally add
|
||
shutter_speeds_add = [50] # microseconds. shutter speeds to additionally add
|
||
# from investigations: ss < 30 are all darker, but the darkness is the same. e.g: ss=5,10,15,20,25 are all the same brightness=Shutter_speed
|
||
# with ss <5 µs (because < 20 µs) one can request the minimum posisble shutter speed
|
||
# it was found, that ss is adjustable in 20 µs-steps from 30 µs ongoing: [10 µs, 30 µs, 50 µs] in this range
|
||
# retrieved ss: 9, 28, 47, 66, 85, 104 µs
|
||
|
||
shutter_speeds = list()
|
||
#for ss in range(START_SHUTTER_SPEED, END_SHUTTER_SPEED+STEP_SHUTTER_SPEED, STEP_SHUTTER_SPEED):
|
||
# shutter_speeds.append(ss)
|
||
#shutter_speeds = shutter_speeds + shutter_speeds_add # concatenate lists
|
||
shutter_speeds = shutter_speeds_add
|
||
shutter_speeds.sort() # sort list
|
||
|
||
# camera_resolutions = [(192, 144), (416,320), (640,480), (960,720), (1280,960), (1648,1232)]
|
||
camera_resolutions = [(192, 144)]
|
||
iso_values = [320]
|
||
|
||
# Define camera settings
|
||
if CAM_NR == "03":
|
||
SENSOR_MODE = 4 # corresponding sensor mode to resolution
|
||
OUTPUT_RESOLUTION = (416, 320) # (1640x1232)/4=(410,308)
|
||
# Camera informations for log
|
||
CAM_DESCRIPTION = "Raspberry Pi Camera Module V2"
|
||
CAM_EAN = "506021437024020"
|
||
elif CAM_NR == "04":
|
||
SENSOR_MODE = 4 # corresponding sensor mode to resolution
|
||
# Camera informations for log
|
||
CAM_DESCRIPTION = "Raspberry Pi NoIR Camera Module V2"
|
||
CAM_EAN = "0640522710898"
|
||
elif CAM_NR == ("01"):
|
||
SENSOR_MODE = 4 # corresponding sensor mode to resolution --> gibt es sowas für camera 1? Ja gleicher Sensor wie Raspberry Pi V1 sensor!
|
||
# Camera informations for log
|
||
CAM_DESCRIPTION = "RPI-SPYCAM"
|
||
CAM_EAN = "4251266701743 "
|
||
elif CAM_NR == ("02"):
|
||
SENSOR_MODE = 4 # corresponding sensor mode to resolution --> gibt es sowas für camera 2? --> Ja gleicher Sensor wie Raspberry Pi V1 sensor!
|
||
# Camera informations for log
|
||
CAM_DESCRIPTION = "RB-Camera_JT"
|
||
CAM_EAN = "4250236815909"
|
||
|
||
|
||
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)
|
||
AWB_GAINS = (1.395, 1.15) # White Balance Gains to have colours read correctly: (red, blue). Int, floar or fraction are valid.
|
||
BRIGHTNESS = 25 # sets the brightness setting of the camera. default is 50. [0-100]
|
||
#the brighter, the brighter the LEDs and the higher the RGB values and vice versa!
|
||
CONTRAST = 100 # sets the contrast setting of the camera. The default value is 0. [-100 ... 100]
|
||
|
||
ISO = 320 # ISO value
|
||
EXPOSURE_MODE = 'off'
|
||
FRAMERATE = 25 # 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
|
||
|
||
|
||
# Dictionary for color patterns
|
||
# r: red, g: green, b: blue, y: yellow, m: magenta, c: cyan
|
||
dict_color_pattern = {
|
||
"01": "rgb",
|
||
"02": "rgy",
|
||
"03": "rgm",
|
||
"04": "rgc",
|
||
"05": "rby",
|
||
"06": "rbm",
|
||
"07": "rbc",
|
||
"08": "rym",
|
||
"09": "ryc",
|
||
"10": "rmc",
|
||
"11": "gby",
|
||
"12": "gbm",
|
||
"13": "gbc",
|
||
"14": "gym",
|
||
"15": "gyc",
|
||
"16": "gmc",
|
||
"17": "bym",
|
||
"18": "byc",
|
||
"19": "bmc",
|
||
"20": "ymc",
|
||
}
|
||
|
||
# Define Funcions
|
||
def take_image_picamera_opencv(shutter_speed, iso, resolution, led_rgb_value):
|
||
colour_string = led_rgb_value
|
||
color_substring_list = [str(x).zfill(2) for x in range(1,21)] # string list to check for color pattern
|
||
|
||
# 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 = 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 # Iso must be set prior fixing the gains (prior setting exposure_mode to off)
|
||
camera.shutter_speed = shutter_speed
|
||
# it was found that, you have to set the right shutter speed at the first initalisation of the current runtime of the program.
|
||
# The gains (analog, digital) will adjust to this set up.
|
||
# After the gains are fixed, they will never change! even if you change the shutter speed during the runtime.
|
||
# To get consistent brightness values, set the right shutter speed at initalisation once.
|
||
|
||
time.sleep(SLEEP_TIME) # wait for iso gains and digital_gain and analog_gain to settle before fixing the gains with exposure_mode = off
|
||
# digital_gain: The value represents the digital gain the camera applies after conversion of the sensor’s analog output.
|
||
# analog_gain: The value represents the analog gain of the sensor prior to digital conversion.
|
||
# digital and analog gain can not be set from the user.
|
||
|
||
camera.exposure_mode = EXPOSURE_MODE
|
||
# exposure_mode value 'off' overrides the ISO setting.
|
||
# For example, 'off' fixes analog_gain and digital_gain entirely,
|
||
# preventing this property from adjusting them when set.
|
||
time.sleep(1)
|
||
camera.brightness = BRIGHTNESS
|
||
camera.contrast = CONTRAST
|
||
# camera.start_preview()
|
||
time.sleep(SLEEP_TIME) # Camera warm-up time to apply settings
|
||
exposure_speed = camera.exposure_speed # settings have to be applied before retrieving the right exposure_speed
|
||
|
||
# cv.namedWindow("Current Frame", cv.WINDOW_NORMAL)
|
||
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
|
||
|
||
#display_image_with_text(image, shutter_speed, framenumber) # show the frame
|
||
|
||
framenumber_to_save = 15 # save frame 15 (no particular reason for frame 15)
|
||
if framenumber == framenumber_to_save: # save desired frame
|
||
now = datetime.now(); d1 = now.strftime("%Y-%m-%d %H-%M-%S")
|
||
print(f"Take picture! iso: {iso} shutter speed: {shutter_speed} µs")
|
||
if "-" in colour_string:
|
||
cv.imwrite(f"{path_saveFolder}/RGB {colour_string}_ss{shutter_speed}_es{exposure_speed}_iso{iso}_b{BRIGHTNESS}_c{CONTRAST}_res{resolution[0]}x{resolution[1]}_Date {d1}.png", image)
|
||
elif any(substring in colour_string for substring in color_substring_list):
|
||
cv.imwrite(f"{path_saveFolder}/cp {colour_string}-{dict_color_pattern[colour_string]}_ss{shutter_speed}_es{exposure_speed}_iso{iso}_b{BRIGHTNESS}_c{CONTRAST}_res{resolution[0]}x{resolution[1]}_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.
|
||
# if cv.waitKey(1) & 0xff == ord('q'):
|
||
# break
|
||
|
||
|
||
def take_image_series_onecolourchannel_per_led(led_rgb_value):
|
||
"""Take image series in one specified colour."""
|
||
s.write(str.encode(led_rgb_value)); response = s.readline(); print(response)
|
||
for i, resolution in enumerate(camera_resolutions):
|
||
print(f'resolution {i+1}/{len(camera_resolutions)}: {resolution}')
|
||
for ss in shutter_speeds:
|
||
for iso in iso_values:
|
||
take_image_picamera_opencv(ss, iso, resolution, led_rgb_value)
|
||
|
||
|
||
def take_image_series_rgb(brightness):
|
||
"""Take image series. One colour per series."""
|
||
t1 = time.perf_counter()
|
||
|
||
print(f'switchting to red')
|
||
led_rgb_value = f"{brightness}-000-000"
|
||
s.write(str.encode(led_rgb_value)); response = s.readline(); print(response)
|
||
for i, resolution in enumerate(camera_resolutions):
|
||
print(f'resolution {i+1}/{len(camera_resolutions)}: {resolution}')
|
||
for ss in shutter_speeds:
|
||
for iso in iso_values:
|
||
take_image_picamera_opencv(ss, iso, resolution, led_rgb_value)
|
||
|
||
print('switchting to green')
|
||
led_rgb_value = f"000-{brightness}-000"
|
||
s.write(str.encode(led_rgb_value)); response = s.readline(); print(response)
|
||
for i, resolution in enumerate(camera_resolutions):
|
||
print(f'resolution {i+1}/{len(camera_resolutions)}: {resolution}')
|
||
for ss in shutter_speeds:
|
||
for iso in iso_values:
|
||
take_image_picamera_opencv(ss, iso, resolution, led_rgb_value)
|
||
|
||
print('switchting to blue')
|
||
led_rgb_value = f"000-000-{brightness}"
|
||
s.write(str.encode(led_rgb_value)); response = s.readline(); print(response)
|
||
for i, resolution in enumerate(camera_resolutions):
|
||
print(f'resolution {i+1}/{len(camera_resolutions)}: {resolution}')
|
||
for ss in shutter_speeds:
|
||
for iso in iso_values:
|
||
take_image_picamera_opencv(ss, iso, resolution, led_rgb_value)
|
||
|
||
t2 = time.perf_counter()
|
||
elapsed_time = round((t2-t1)/60,2)
|
||
print(f'series_rgb finished in {elapsed_time} min')
|
||
|
||
def take_image_series_ymc(brightness):
|
||
"""Take image series. One colour per series."""
|
||
t1 = time.perf_counter()
|
||
|
||
print(f'switchting to yellow')
|
||
led_rgb_value = f"{brightness}-{brightness}-000"
|
||
s.write(str.encode(led_rgb_value)); response = s.readline(); print(response)
|
||
for i, resolution in enumerate(camera_resolutions):
|
||
print(f'resolution {i+1}/{len(camera_resolutions)}: {resolution}')
|
||
for ss in shutter_speeds:
|
||
for iso in iso_values:
|
||
take_image_picamera_opencv(ss, iso, resolution, led_rgb_value)
|
||
|
||
print('switchting to magenta')
|
||
led_rgb_value = f"{brightness}-000-{brightness}"
|
||
s.write(str.encode(led_rgb_value)); response = s.readline(); print(response)
|
||
for i, resolution in enumerate(camera_resolutions):
|
||
print(f'resolution {i+1}/{len(camera_resolutions)}: {resolution}')
|
||
for ss in shutter_speeds:
|
||
for iso in iso_values:
|
||
take_image_picamera_opencv(ss, iso, resolution, led_rgb_value)
|
||
|
||
print('switchting to cyan')
|
||
led_rgb_value = f"000-{brightness}-{brightness}"
|
||
s.write(str.encode(led_rgb_value)); response = s.readline(); print(response)
|
||
for i, resolution in enumerate(camera_resolutions):
|
||
print(f'resolution {i+1}/{len(camera_resolutions)}: {resolution}')
|
||
for ss in shutter_speeds:
|
||
for iso in iso_values:
|
||
take_image_picamera_opencv(ss, iso, resolution, led_rgb_value)
|
||
|
||
t2 = time.perf_counter()
|
||
elapsed_time = round((t2-t1)/60,2)
|
||
print(f'series_rgb finished in {elapsed_time} min')
|
||
|
||
|
||
def take_image_series_twocolourchannels_per_led(brightness):
|
||
"""Take image series. Two colours per series."""
|
||
# All posibilities with R G B: RG, RB, GB
|
||
|
||
print(f'switchting to red and green')
|
||
led_rgb_value = f"{brightness}-{brightness}-000"
|
||
s.write(str.encode(led_rgb_value)); response = s.readline(); print(response)
|
||
for ss in range(START_SHUTTER_SPEED, END_SHUTTER_SPEED+STEP_SHUTTER_SPEED, STEP_SHUTTER_SPEED):
|
||
take_image_picamera_opencv(ss, led_rgb_value)
|
||
|
||
print('switchting to red and blue')
|
||
led_rgb_value = f"{brightness}-000-{brightness}"
|
||
s.write(str.encode(led_rgb_value)); response = s.readline(); print(response)
|
||
for ss in range(START_SHUTTER_SPEED, END_SHUTTER_SPEED+STEP_SHUTTER_SPEED, STEP_SHUTTER_SPEED):
|
||
take_image_picamera_opencv(ss, led_rgb_value)
|
||
|
||
print('switchting to green and blue')
|
||
led_rgb_value = f"000-{brightness}-{brightness}"
|
||
s.write(str.encode(led_rgb_value)); response = s.readline(); print(response)
|
||
for ss in range(START_SHUTTER_SPEED, END_SHUTTER_SPEED+STEP_SHUTTER_SPEED, STEP_SHUTTER_SPEED):
|
||
take_image_picamera_opencv(ss, led_rgb_value)
|
||
|
||
def takeimage_all_color_patterns():
|
||
"""Take images of all 20 color patterns including orientation """
|
||
"The brightness is hard coded to 255 in the Arduino"
|
||
|
||
t1 = time.perf_counter()
|
||
|
||
print(f'take images for all 20 color patterns')
|
||
for number_of_color_pattern in range(1,21):
|
||
number_of_color_pattern = str(number_of_color_pattern).zfill(2)
|
||
print(f"color pattern no: {number_of_color_pattern}: {dict_color_pattern[number_of_color_pattern]}")
|
||
s.write(str.encode(f"color_pattern_{number_of_color_pattern}"))
|
||
|
||
for i, resolution in enumerate(camera_resolutions):
|
||
print(f'resolution {i+1}/{len(camera_resolutions)}: {resolution}')
|
||
for ss in shutter_speeds:
|
||
for iso in iso_values:
|
||
take_image_picamera_opencv(ss, iso, resolution, number_of_color_pattern)
|
||
|
||
t2 = time.perf_counter()
|
||
elapsed_time = round((t2-t1)/60,2)
|
||
print(f'series_rgb finished in {elapsed_time} min')
|
||
|
||
|
||
def display_image_with_text(img, shutter_speed, framenumber):
|
||
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
|
||
|
||
# set position in (x,y)-coordinated from top left corner. Bottom-left corner of the text string in the image.
|
||
pos_1 = (int(frame_width/4), text_start_position_Y) # start text from 1/4 of image width
|
||
pos_2 = (int(frame_width/4), text_start_position_Y+text_linespacing) # start text from 1/4 of image width
|
||
|
||
# define text to display
|
||
text_line_1 = f"Shutterspeed: {shutter_speed} us"
|
||
text_line_2 = f"Frame: {framenumber}"
|
||
|
||
# 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)
|
||
|
||
cv.imshow("Current Frame", img) # display the image
|
||
|
||
|
||
# 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"/Solarradiation_"+d1
|
||
path_saveFolder = path_cwd+r"/series_"+d1
|
||
try:
|
||
os.mkdir(path_saveFolder)
|
||
lines = [f"CAMERA NR: {CAM_NR}", f"CAMERA DESCRIPTION: {CAM_DESCRIPTION}", f"CAMERA EAN: {CAM_EAN}",
|
||
f"\n#Camera settings:", f"SENSOR MODE: {SENSOR_MODE}",
|
||
f"FRAMERATE: {FRAMERATE} # frames per second",
|
||
f"AWB_MODE: {AWB_MODE}", f"AWB_GAINS (red, blue): {AWB_GAINS}",
|
||
f"Brightness: {BRIGHTNESS}", f"Contrast: {CONTRAST}",
|
||
f"EXPOSURE_MODE: {EXPOSURE_MODE}",
|
||
f"\nCOMMENT: {COMMENT}"]
|
||
with open(os.path.join(path_saveFolder, "log.txt"), "w") as f:
|
||
f.write("\n".join(lines))
|
||
except OSError:
|
||
print("Error! Ending script. Try it again in 1 minute")
|
||
quit()
|
||
|
||
# Turn on LEDs
|
||
s = serial.Serial("/dev/ttyACM0", 9600) # Name of USB-Port may vary (here: ACM0)
|
||
time.sleep(5) # der Arduino resettet nach einer Seriellen Verbindung, daher muss kurz gewartet werden
|
||
|
||
|
||
|
||
# start capture series for different shutter speeds
|
||
print('start caputure series...')
|
||
|
||
|
||
# s.write(str.encode("000-255-000")); response = s.readline(); print(response)
|
||
# take_image_picamera_opencv(shutter_speed=200, led_rgb_value="000-255-000")
|
||
|
||
|
||
# take image series for two colour channels per led
|
||
# for brgns in range(50,260,50):
|
||
# brgns=str(brgns).zfill(3)
|
||
# take_image_series_twocolourchannels_per_led(brgns) #Take Image Series for every color in given brightness
|
||
# take_image_series_twocolourchannels_per_led(255) #Take Image Series for every color in given brightness
|
||
|
||
|
||
# # take image series for white colour
|
||
# for brgns in range(50,260,50):
|
||
# brgns=f"{brgns}".zfill(3)
|
||
# ledcolour=f"{brgns}-{brgns}-{brgns}"
|
||
# take_image_series_onecolourchannel_per_led(ledcolour)
|
||
# take_image_series_onecolourchannel_per_led("255-255-255")
|
||
|
||
# --------------------------------------------------------------------------------------------------------------------------------------------
|
||
# # 03.02.2022: Take images with multiple shutter speeds but in brightness 255 for determining colour falsification to the edges of the image
|
||
# take_image_series_rgb(255)
|
||
# take_image_series_ymc(255)
|
||
# take_image_series_onecolourchannel_per_led("255-255-255")
|
||
|
||
# --------------------------------------------------------------------------------------------------------------------------------------------
|
||
# # 26.02.2022: Take images with multiple shutter speeds and brightness for investigating the influence of the solar radiation on the images
|
||
# # take image series for rgb
|
||
# for brgns in range(50,260,50):
|
||
# brgns=str(brgns).zfill(3)
|
||
# take_image_series_ymc(brgns) #Take Image Series for every color in given brightness
|
||
# take_image_series_ymc(255) #Take Image Series for every color in given brightness
|
||
|
||
# # take image series for ymc
|
||
# for brgns in range(50,260,50):
|
||
# brgns=str(brgns).zfill(3)
|
||
# take_image_series_rgb(brgns) #Take Image Series for every color in given brightness
|
||
# take_image_series_rgb(255) #Take Image Series for every color in given brightness
|
||
|
||
# # take image series for white colour
|
||
# for brgns in range(50,260,50):
|
||
# brgns=f"{brgns}".zfill(3)
|
||
# ledcolour=f"{brgns}-{brgns}-{brgns}"
|
||
# take_image_series_onecolourchannel_per_led(ledcolour)
|
||
# take_image_series_onecolourchannel_per_led("255-255-255")
|
||
# --------------------------------------------------------------------------------------------------------------------------------------------
|
||
# 10.04.2022: Take images with multiple shutter speeds for investigating the influence of the solar radiation on the images
|
||
# take image series for rgb
|
||
take_image_series_rgb(255) #Take Image Series for every color in given brightness
|
||
|
||
# take image series for ymc
|
||
take_image_series_ymc(255) #Take Image Series for every color in given brightness
|
||
|
||
# take image series for white colour
|
||
take_image_series_onecolourchannel_per_led("255-255-255")
|
||
# --------------------------------------------------------------------------------------------------------------------------------------------
|
||
# 07.03.2022: Take images of all 20 color patterns with birghtness 255
|
||
takeimage_all_color_patterns()
|
||
# --------------------------------------------------------------------------------------------------------------------------------------------
|
||
|
||
|
||
# End Script
|
||
|
||
# Turn off LEDs
|
||
s.write(str.encode('000-000-000')); response = s.readline(); print(response)
|
||
s.close() #close serial port
|
||
|
||
cv.destroyAllWindows()
|
||
|
||
t1 = round(time.perf_counter()/60,2)
|
||
|
||
print(f'Script finished in {t1} min')
|
||
|
||
|
||
|
||
|
||
|
||
|