238 lines
13 KiB
Python
238 lines
13 KiB
Python
|
# Date: 18.12.2021
|
||
|
# Author: Kenan Gömek
|
||
|
# Das Skript stellt die unterschiedlichen Farbkanäle der aufgenommenen Bilder dar und erstellt schnitte, um die Pixelwerte anzuzeigen.
|
||
|
# Update: 18.02.2021
|
||
|
# Update Comment: Multiprocessing
|
||
|
|
||
|
# Import needed Packages
|
||
|
import cv2 as cv
|
||
|
import numpy as np
|
||
|
import time
|
||
|
from datetime import datetime
|
||
|
import os
|
||
|
from matplotlib import pyplot as plt
|
||
|
|
||
|
from multiprocessing import Process, cpu_count, Pool
|
||
|
|
||
|
# define User Input
|
||
|
# No vowels in path name!
|
||
|
PATH_IMAGE_FOLDER = r'U:\bwsyncshare\Auswertung'
|
||
|
|
||
|
COMMENT = "" # User comment for plot
|
||
|
|
||
|
|
||
|
# define functions
|
||
|
def calc_arithmetic_mean_of_brightness_per_pixel(r, g, b):
|
||
|
"""Calculate overall brightness per pixel of the image. Mittelere Helligkeit pro pixel berechnen."""
|
||
|
r = r.astype('uint16') # set dtype of one colour to uint16, because the sum of 255+255+255 >255 =765
|
||
|
# the rest will also be uint16 then
|
||
|
image_heigth = r.shape[0]
|
||
|
image_width = r.shape[1]
|
||
|
|
||
|
number_of_colour_channels = 3
|
||
|
arithmetic_mean_of_brightness_image = np.sum((r+g+b)/number_of_colour_channels)
|
||
|
arithmetic_mean_of_brightness_per_pixel = arithmetic_mean_of_brightness_image/(image_width*image_heigth)
|
||
|
|
||
|
max_possible_brightness = 255 # maximum possible brightness for an uint8
|
||
|
arithmetic_mean_of_brightness_per_pixel_relative = arithmetic_mean_of_brightness_per_pixel/max_possible_brightness # range: 0-1
|
||
|
|
||
|
return arithmetic_mean_of_brightness_per_pixel_relative
|
||
|
|
||
|
def image_analysis(image_name, image_number, number_of_total_images, comment):
|
||
|
print(f"Current image {image_number+1}/{number_of_total_images}: {image_name}")
|
||
|
|
||
|
# Split and Merge colour channels
|
||
|
img_bgr = cv.imread(os.path.join(PATH_IMAGE_FOLDER,image_name), cv.IMREAD_COLOR)
|
||
|
b,g,r = cv.split(img_bgr)
|
||
|
img_rgb = img_bgr[:, :, ::-1]
|
||
|
|
||
|
# Create Cuts and display Pixel Values
|
||
|
image_width = r.shape[1]
|
||
|
if image_width < 1000:
|
||
|
plot_marker_offset = 4 # Thickness of marker-line in plot in pixels
|
||
|
elif image_width >= 1000:
|
||
|
plot_marker_offset = 10 # Thickness of marker-line in plot in pixels
|
||
|
|
||
|
|
||
|
# Identify dominating colour channel and set cut row and column
|
||
|
max_pixelvalue_red = r.max(); max_pixelvalue_green = g.max(); max_pixelvalue_blue = b.max()
|
||
|
max_pixelvalue = np.max([max_pixelvalue_red, max_pixelvalue_green, max_pixelvalue_blue])
|
||
|
|
||
|
idx_max_pixelvalue_red = np.unravel_index(r.argmax(), r.shape) #row=idx_..[0] column=idx_..[1]
|
||
|
idx_max_pixelvalue_green = np.unravel_index(g.argmax(), g.shape) #row=idx_..[0] column=idx_..[1]
|
||
|
idx_max_pixelvalue_blue = np.unravel_index(b.argmax(), b.shape) #row=idx_..[0] column=idx_..[1]
|
||
|
if max_pixelvalue == max_pixelvalue_red:
|
||
|
idx_max_pixelvalue = idx_max_pixelvalue_red; msg_dominating_colourchannel = 'red'
|
||
|
elif max_pixelvalue == max_pixelvalue_green:
|
||
|
idx_max_pixelvalue = idx_max_pixelvalue_green; msg_dominating_colourchannel = 'green'
|
||
|
elif max_pixelvalue == max_pixelvalue_blue:
|
||
|
idx_max_pixelvalue = idx_max_pixelvalue_blue; msg_dominating_colourchannel = 'blue'
|
||
|
|
||
|
|
||
|
cut_row=idx_max_pixelvalue[0]; cut_column=idx_max_pixelvalue[1]
|
||
|
|
||
|
|
||
|
# Red channel
|
||
|
# Info linspace(start, stop, num):
|
||
|
# stop: The end value of the sequence --> stop is included
|
||
|
# num: Number of samples to generate.
|
||
|
# Because Array stars at index 0: e.g. image with 3280x2464 Pixels: for 3280 Pixels you need 3280 values between 0 and 3279
|
||
|
# check: x_red_h must have 3280 values?? --> Yes from 0 to 3279 in 1 steps
|
||
|
x_red_h = np.linspace(0, r.shape[1]-1, r.shape[1]); y_red_h = r[cut_row,:] # data horizontal cut ->width (e.g.: 3280)
|
||
|
x_red_v = np.linspace(0, r.shape[0]-1, r.shape[0]); y_red_v = r[:,cut_column] # data vertical cut ->height (e.g.: 2464)
|
||
|
|
||
|
msg1_red = f"Maximum Pixel value in red channel: {max_pixelvalue_red}"; print(msg1_red)
|
||
|
msg2_red = f"Index of max Value in red channel (row, colum): {idx_max_pixelvalue_red}"; print(msg2_red)
|
||
|
msg3_red = f"Maximum Pixel value in marked-row {cut_row}: {np.max(y_red_h)}"; print(msg3_red)
|
||
|
msg4_red = f"Maximum Pixel value in marked-column {cut_column}: {np.max(y_red_v)}"; print(msg4_red)
|
||
|
r_copy = r.copy(); r_copy[cut_row:cut_row+plot_marker_offset,:]=255; r_copy[:, cut_column:cut_column+plot_marker_offset]=255 # manipulate image for displaying in marked plot
|
||
|
|
||
|
# Green channel
|
||
|
x_green_h = np.linspace(0, g.shape[1]-1, g.shape[1]); y_green_h = g[cut_row,:] # data horizontal cut
|
||
|
x_green_v = np.linspace(0, g.shape[0]-1, g.shape[0]); y_green_v = g[:,cut_column] # data vertical cut
|
||
|
|
||
|
msg1_green = f"Maximum Pixel value in green channel: {max_pixelvalue_green}"; print(msg1_green)
|
||
|
msg2_green = f"Index of max Value in green channel (row, colum): {idx_max_pixelvalue_green}"; print(msg2_green)
|
||
|
msg3_green = f"Maximum Pixel value in marked-row {cut_row}: {np.max(y_green_h)}"; print(msg3_green)
|
||
|
msg4_green = f"Maximum Pixel value in marked-column {cut_column}: {np.max(y_green_v)}"; print(msg4_green)
|
||
|
g_copy = g.copy(); g_copy[cut_row:cut_row+plot_marker_offset,:]=255; g_copy[:, cut_column:cut_column+plot_marker_offset]=255 # manipulate image for displaying in marked plot
|
||
|
|
||
|
# Blue channel
|
||
|
x_blue_h = np.linspace(0, b.shape[1]-1, b.shape[1]); y_blue_h = b[cut_row,:] # data horizontal cut
|
||
|
x_blue_v = np.linspace(0, b.shape[0]-1, b.shape[0]); y_blue_v = b[:,cut_column] # data vertical cut
|
||
|
|
||
|
msg1_blue = f"Maximum Pixel value in blue channel: {max_pixelvalue_blue}"; print(msg1_blue)
|
||
|
msg2_blue = f"Index of max Value in blue channel (row, colum): {idx_max_pixelvalue_blue}"; print(msg2_blue)
|
||
|
msg3_blue = f"Maximum Pixel value in marked-row {cut_row}: {np.max(y_blue_h)}"; print(msg3_blue)
|
||
|
msg4_blue = f"Maximum Pixel value in marked-column {cut_column}: {np.max(y_blue_v)}"; print(msg4_blue)
|
||
|
b_copy = b.copy(); b_copy[cut_row:cut_row+plot_marker_offset,:]=255; b_copy[:, cut_column:cut_column+plot_marker_offset]=255 # manipulate image for displaying in marked plot
|
||
|
|
||
|
# Create Plots
|
||
|
fig1, ((ax_orig_1,ax01,ax02,ax03),(ax_red_1, ax_red_2, ax_red_3, ax_red_4),
|
||
|
(ax_green_1, ax_green_2, ax_green_3, ax_green_4),(ax_blue_1, ax_blue_2, ax_blue_3, ax_blue_4)) \
|
||
|
= plt.subplots(4, 4, figsize=(30,25))
|
||
|
fig1.suptitle(f'Image: {image_name}', y=0.9)
|
||
|
|
||
|
yticks=np.append(np.arange(0,230,25), 255) # set yticks for cuts
|
||
|
xlim_max_h=r.shape[1] # xlim for horizontal cut. No special reason for choosing red channel.
|
||
|
xlim_max_v=r.shape[0] # xlim for vertical cut. No special reason for choosing red channel.
|
||
|
|
||
|
ax_orig_1.imshow(img_rgb); ax_orig_1.set_title("Original Image");
|
||
|
ax_orig_1.set_xlabel('Width=H=Columns'); ax_orig_1.set_ylabel('Heigth=V=Rows')
|
||
|
|
||
|
# red channel
|
||
|
ax_red_1.imshow(r, cmap = 'gray'); ax_red_1.set_title("Red Channel");
|
||
|
ax_red_1.set_xlabel('Width=H=Columns'); ax_red_1.set_ylabel('Heigth=V=Rows')
|
||
|
ax_red_2.imshow(r_copy, cmap = 'gray'); ax_red_2.set_title("Red Channel - marked");
|
||
|
ax_red_2.set_xlabel('Width=H=Columns'); ax_red_2.set_ylabel('Heigth=V=Rows')
|
||
|
ax_red_3.plot(x_red_h,y_red_h, linewidth=2.0); ax_red_3.set_title("Horizontal Cut");
|
||
|
ax_red_3.grid(True); ax_red_3.set_ylim(ymin=0, ymax=260); ax_red_3.set_yticks(yticks); ax_red_3.set_xlim(0,xlim_max_h)
|
||
|
ax_red_3.set_xlabel('Width=H=Columns'); ax_red_3.set_ylabel('Pixel Value')
|
||
|
ax_red_4.plot(x_red_v,y_red_v, linewidth=2.0); ax_red_4.set_title("Vertical Cut");
|
||
|
ax_red_4.grid(True); ax_red_4.set_ylim(ymin=0, ymax=260); ax_red_4.set_yticks(yticks); ax_red_4.set_xlim(0, xlim_max_v)
|
||
|
ax_red_4.set_xlabel('Heigth=V=Rows'); ax_red_4.set_ylabel('Pixel Value')
|
||
|
|
||
|
# green channel
|
||
|
ax_green_1.imshow(g, cmap = 'gray'); ax_green_1.set_title("Green Channel");
|
||
|
ax_green_1.set_xlabel('Width=H=Columns'); ax_green_1.set_ylabel('Heigth=V=Rows')
|
||
|
ax_green_2.imshow(g_copy, cmap = 'gray'); ax_green_2.set_title("Green Channel - marked");
|
||
|
ax_green_2.set_xlabel('Width=H=Columns'); ax_green_2.set_ylabel('Heigth=V=Rows')
|
||
|
ax_green_3.plot(x_green_h,y_green_h, linewidth=2.0); ax_green_3.set_title("Horizontal Cut");
|
||
|
ax_green_3.grid(True); ax_green_3.set_ylim(ymin=0, ymax=260); ax_green_3.set_yticks(yticks); ax_green_3.set_xlim(0,xlim_max_h)
|
||
|
ax_green_3.set_xlabel('Width=H=Columns'); ax_green_3.set_ylabel('Pixel Value')
|
||
|
ax_green_4.plot(x_green_v,y_green_v, linewidth=2.0); ax_green_4.set_title("Vertical Cut");
|
||
|
ax_green_4.grid(True); ax_green_4.set_ylim(ymin=0, ymax=260); ax_green_4.set_yticks(yticks); ax_green_4.set_xlim(0, xlim_max_v)
|
||
|
ax_green_4.set_xlabel('Heigth=V=Rows'); ax_green_4.set_ylabel('Pixel Value')
|
||
|
|
||
|
# blue channel
|
||
|
ax_blue_1.imshow(b, cmap = 'gray'); ax_blue_1.set_title("Blue Channel");
|
||
|
ax_blue_1.set_xlabel('Width=H=Columns'); ax_blue_1.set_ylabel('Heigth=V=Rows')
|
||
|
ax_blue_2.imshow(b_copy, cmap = 'gray'); ax_blue_2.set_title("Blue Channel - marked");
|
||
|
ax_blue_2.set_xlabel('Width=H=Columns'); ax_blue_2.set_ylabel('Heigth=V=Rows')
|
||
|
ax_blue_3.plot(x_blue_h,y_blue_h, linewidth=2.0); ax_blue_3.set_title("Horizontal Cut");
|
||
|
ax_blue_3.grid(True); ax_blue_3.set_ylim(ymin=0, ymax=260); ax_blue_3.set_yticks(yticks); ax_blue_3.set_xlim(0,xlim_max_h)
|
||
|
ax_blue_3.set_xlabel('Width=H=Columns'); ax_blue_3.set_ylabel('Pixel Value')
|
||
|
ax_blue_4.plot(x_blue_v,y_blue_v, linewidth=2.0); ax_blue_4.set_title("Vertical Cut");
|
||
|
ax_blue_4.grid(True); ax_blue_4.set_ylim(ymin=0, ymax=260); ax_blue_4.set_yticks(yticks); ax_blue_4.set_xlim(0, xlim_max_v)
|
||
|
ax_blue_4.set_xlabel('Heigth=V=Rows'); ax_blue_4.set_ylabel('Pixel Value')
|
||
|
|
||
|
|
||
|
# Calculate overall brightness per pixel of the image. Mittelere Helligkeit pro pixel berechnen.
|
||
|
arithmetic_mean_of_brightness_per_pixel_relative = calc_arithmetic_mean_of_brightness_per_pixel(r,g,b)
|
||
|
if arithmetic_mean_of_brightness_per_pixel_relative >= 0.01:
|
||
|
msg1_brightness = f"Overall brightness per pixel: {round(arithmetic_mean_of_brightness_per_pixel_relative*100,2)} %" #value in percent
|
||
|
elif arithmetic_mean_of_brightness_per_pixel_relative < 0.01:
|
||
|
msg1_brightness = f"Overall brightness per pixel: {round(arithmetic_mean_of_brightness_per_pixel_relative*10e3,2)} per mil" # value in promille
|
||
|
print(msg1_brightness)
|
||
|
|
||
|
# add pixel stats under Figure
|
||
|
pixelstats_red= '\n'.join([msg1_red, msg2_red, msg3_red, msg4_red])
|
||
|
pixelstats_green= '\n'.join([msg1_green, msg2_green, msg3_green, msg4_green])
|
||
|
pixelstats_blue= '\n'.join([msg1_blue, msg2_blue, msg3_blue, msg4_blue])
|
||
|
pixelstats_overall_brightness = '\n'.join([msg1_brightness])
|
||
|
pixelstats = '\n\n'.join([f"pixel stats: {msg_dominating_colourchannel} channel dominating",
|
||
|
pixelstats_red, pixelstats_green, pixelstats_blue, pixelstats_overall_brightness])
|
||
|
text_x_pos = 0.1; text_y_pos = -0.015 # text position: 0,0 is lower-left and 1,1 is upper-right)
|
||
|
fig1.text(text_x_pos, text_y_pos, pixelstats, ha='left')
|
||
|
|
||
|
# add Comment under Figure
|
||
|
text_x_pos = 0.5; text_y_pos = -0.025 # text position: 0,0 is lower-left and 1,1 is upper-right)
|
||
|
log_filename=None
|
||
|
try:
|
||
|
log_filename=[f for f in os.listdir(PATH_IMAGE_FOLDER) if f.endswith('.txt')][0]
|
||
|
if log_filename:
|
||
|
with open(os.path.join(PATH_IMAGE_FOLDER,log_filename), encoding='utf-8') as f:
|
||
|
log_text = f.readlines()
|
||
|
if comment != "":
|
||
|
comment = '\nPlot Comment: '+comment
|
||
|
log_text.append(comment)
|
||
|
txt=''.join(log_text)
|
||
|
fig1.text(text_x_pos, text_y_pos, txt, ha='left')
|
||
|
except IndexError:
|
||
|
if comment != "":
|
||
|
comment = '\nPlot Comment: '+comment
|
||
|
fig1.text(text_x_pos, text_y_pos, comment, ha='left')
|
||
|
else:
|
||
|
pass
|
||
|
|
||
|
# handle numpy memmory error on Windows:
|
||
|
switch_overwrite=0 # do not overwrite files, if they exist
|
||
|
if switch_overwrite == 1:
|
||
|
fig1.savefig(os.path.join(PATH_IMAGE_FOLDER,f'{image_name}.pdf'), bbox_inches='tight') #save plot
|
||
|
print('Save pdf')
|
||
|
else:
|
||
|
if os.path.isfile(os.path.join(PATH_IMAGE_FOLDER,f'{image_name}.pdf')):
|
||
|
pass # skip this pdf file, because it already exists
|
||
|
else:
|
||
|
fig1.savefig(os.path.join(PATH_IMAGE_FOLDER,f'{image_name}.pdf'), bbox_inches='tight') #save plot
|
||
|
print('Save pdf')
|
||
|
plt.close('all') # close all figures
|
||
|
|
||
|
print('') # new line for better readability in console
|
||
|
|
||
|
|
||
|
# start
|
||
|
def main():
|
||
|
number_of_CPUS = cpu_count()
|
||
|
|
||
|
image_filenames=[f for f in os.listdir(PATH_IMAGE_FOLDER) if f.endswith('.png')]
|
||
|
image_filenames.sort()
|
||
|
list_numbers = [x for x in range(len(image_filenames))]
|
||
|
number_of_total_images = len(image_filenames)
|
||
|
list_number_of_total_images = [number_of_total_images]*number_of_total_images # list with n times number of total images
|
||
|
list_COMMENT = [COMMENT]*number_of_total_images
|
||
|
|
||
|
print(number_of_total_images)
|
||
|
print(len(list_numbers))
|
||
|
|
||
|
data_to_pass = list(zip(image_filenames, list_numbers, list_number_of_total_images, list_COMMENT))
|
||
|
with Pool(number_of_CPUS) as pool:
|
||
|
pool.starmap(image_analysis, iterable=data_to_pass)
|
||
|
|
||
|
t1 = round(time.perf_counter()/60,2)
|
||
|
print(f'Script finished in {t1} min')
|
||
|
|
||
|
if __name__ == '__main__':
|
||
|
main()
|
||
|
|