mirror of
https://git.libcamera.org/libcamera/libcamera.git
synced 2025-07-13 15:29:45 +03:00
utils: raspberrypi: ctt: Adapt tuning tool for both VC4 and PiSP
The old ctt.py and alsc_only.py scripts are removed. Instead of ctt.py use ctt_vc4.py or ctt_pisp.py, depending on your target platform. Instead of alsc_only.py use alsc_vc4.py or alsc_pisp.py, again according to your platform. Signed-off-by: David Plowman <david.plowman@raspberrypi.com> Reviewed-by: Naushir Patuck <naush@raspberrypi.com> Tested-by: Naushir Patuck <naush@raspberrypi.com> Acked-by: Kieran Bingham <kieran.bingham@ideasonboard.com> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
This commit is contained in:
parent
634bc7838f
commit
d13542c28f
9 changed files with 511 additions and 208 deletions
37
utils/raspberrypi/ctt/alsc_pisp.py
Executable file
37
utils/raspberrypi/ctt/alsc_pisp.py
Executable file
|
@ -0,0 +1,37 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
#
|
||||||
|
# Copyright (C) 2022, Raspberry Pi (Trading) Limited
|
||||||
|
#
|
||||||
|
# alsc_only.py - alsc tuning tool
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from ctt_pisp import json_template, grid_size, target
|
||||||
|
from ctt_run import run_ctt
|
||||||
|
from ctt_tools import parse_input
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
"""
|
||||||
|
initialise calibration
|
||||||
|
"""
|
||||||
|
if len(sys.argv) == 1:
|
||||||
|
print("""
|
||||||
|
PiSP Lens Shading Camera Tuning Tool version 1.0
|
||||||
|
|
||||||
|
Required Arguments:
|
||||||
|
'-i' : Calibration image directory.
|
||||||
|
'-o' : Name of output json file.
|
||||||
|
|
||||||
|
Optional Arguments:
|
||||||
|
'-c' : Config file for the CTT. If not passed, default parameters used.
|
||||||
|
'-l' : Name of output log file. If not passed, 'ctt_log.txt' used.
|
||||||
|
""")
|
||||||
|
quit(0)
|
||||||
|
else:
|
||||||
|
"""
|
||||||
|
parse input arguments
|
||||||
|
"""
|
||||||
|
json_output, directory, config, log_output = parse_input()
|
||||||
|
run_ctt(json_output, directory, config, log_output, json_template, grid_size, target, alsc_only=True)
|
|
@ -6,8 +6,11 @@
|
||||||
#
|
#
|
||||||
# alsc tuning tool
|
# alsc tuning tool
|
||||||
|
|
||||||
from ctt import *
|
import sys
|
||||||
|
|
||||||
|
from ctt_vc4 import json_template, grid_size, target
|
||||||
|
from ctt_run import run_ctt
|
||||||
|
from ctt_tools import parse_input
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
"""
|
"""
|
||||||
|
@ -15,7 +18,7 @@ if __name__ == '__main__':
|
||||||
"""
|
"""
|
||||||
if len(sys.argv) == 1:
|
if len(sys.argv) == 1:
|
||||||
print("""
|
print("""
|
||||||
Pisp Camera Tuning Tool version 1.0
|
VC4 Lens Shading Camera Tuning Tool version 1.0
|
||||||
|
|
||||||
Required Arguments:
|
Required Arguments:
|
||||||
'-i' : Calibration image directory.
|
'-i' : Calibration image directory.
|
||||||
|
@ -31,4 +34,4 @@ if __name__ == '__main__':
|
||||||
parse input arguments
|
parse input arguments
|
||||||
"""
|
"""
|
||||||
json_output, directory, config, log_output = parse_input()
|
json_output, directory, config, log_output = parse_input()
|
||||||
run_ctt(json_output, directory, config, log_output, alsc_only=True)
|
run_ctt(json_output, directory, config, log_output, json_template, grid_size, target, alsc_only=True)
|
|
@ -13,8 +13,9 @@ from mpl_toolkits.mplot3d import Axes3D
|
||||||
"""
|
"""
|
||||||
preform alsc calibration on a set of images
|
preform alsc calibration on a set of images
|
||||||
"""
|
"""
|
||||||
def alsc_all(Cam, do_alsc_colour, plot):
|
def alsc_all(Cam, do_alsc_colour, plot, grid_size=(16, 12)):
|
||||||
imgs_alsc = Cam.imgs_alsc
|
imgs_alsc = Cam.imgs_alsc
|
||||||
|
grid_w, grid_h = grid_size
|
||||||
"""
|
"""
|
||||||
create list of colour temperatures and associated calibration tables
|
create list of colour temperatures and associated calibration tables
|
||||||
"""
|
"""
|
||||||
|
@ -23,7 +24,7 @@ def alsc_all(Cam, do_alsc_colour, plot):
|
||||||
list_cb = []
|
list_cb = []
|
||||||
list_cg = []
|
list_cg = []
|
||||||
for Img in imgs_alsc:
|
for Img in imgs_alsc:
|
||||||
col, cr, cb, cg, size = alsc(Cam, Img, do_alsc_colour, plot)
|
col, cr, cb, cg, size = alsc(Cam, Img, do_alsc_colour, plot, grid_size=grid_size)
|
||||||
list_col.append(col)
|
list_col.append(col)
|
||||||
list_cr.append(cr)
|
list_cr.append(cr)
|
||||||
list_cb.append(cb)
|
list_cb.append(cb)
|
||||||
|
@ -68,11 +69,12 @@ def alsc_all(Cam, do_alsc_colour, plot):
|
||||||
t_b = np.where((100*t_b) % 1 >= 0.95, t_b-0.001, t_b)
|
t_b = np.where((100*t_b) % 1 >= 0.95, t_b-0.001, t_b)
|
||||||
t_r = np.round(t_r, 3)
|
t_r = np.round(t_r, 3)
|
||||||
t_b = np.round(t_b, 3)
|
t_b = np.round(t_b, 3)
|
||||||
r_corners = (t_r[0], t_r[15], t_r[-1], t_r[-16])
|
r_corners = (t_r[0], t_r[grid_w - 1], t_r[-1], t_r[-grid_w])
|
||||||
b_corners = (t_b[0], t_b[15], t_b[-1], t_b[-16])
|
b_corners = (t_b[0], t_b[grid_w - 1], t_b[-1], t_b[-grid_w])
|
||||||
r_cen = t_r[5*16+7]+t_r[5*16+8]+t_r[6*16+7]+t_r[6*16+8]
|
middle_pos = (grid_h // 2 - 1) * grid_w + grid_w - 1
|
||||||
|
r_cen = t_r[middle_pos]+t_r[middle_pos + 1]+t_r[middle_pos + grid_w]+t_r[middle_pos + grid_w + 1]
|
||||||
r_cen = round(r_cen/4, 3)
|
r_cen = round(r_cen/4, 3)
|
||||||
b_cen = t_b[5*16+7]+t_b[5*16+8]+t_b[6*16+7]+t_b[6*16+8]
|
b_cen = t_b[middle_pos]+t_b[middle_pos + 1]+t_b[middle_pos + grid_w]+t_b[middle_pos + grid_w + 1]
|
||||||
b_cen = round(b_cen/4, 3)
|
b_cen = round(b_cen/4, 3)
|
||||||
Cam.log += '\nRed table corners: {}'.format(r_corners)
|
Cam.log += '\nRed table corners: {}'.format(r_corners)
|
||||||
Cam.log += '\nRed table centre: {}'.format(r_cen)
|
Cam.log += '\nRed table centre: {}'.format(r_cen)
|
||||||
|
@ -116,8 +118,9 @@ def alsc_all(Cam, do_alsc_colour, plot):
|
||||||
"""
|
"""
|
||||||
calculate g/r and g/b for 32x32 points arranged in a grid for a single image
|
calculate g/r and g/b for 32x32 points arranged in a grid for a single image
|
||||||
"""
|
"""
|
||||||
def alsc(Cam, Img, do_alsc_colour, plot=False):
|
def alsc(Cam, Img, do_alsc_colour, plot=False, grid_size=(16, 12)):
|
||||||
Cam.log += '\nProcessing image: ' + Img.name
|
Cam.log += '\nProcessing image: ' + Img.name
|
||||||
|
grid_w, grid_h = grid_size
|
||||||
"""
|
"""
|
||||||
get channel in correct order
|
get channel in correct order
|
||||||
"""
|
"""
|
||||||
|
@ -128,24 +131,24 @@ def alsc(Cam, Img, do_alsc_colour, plot=False):
|
||||||
where w is a multiple of 32.
|
where w is a multiple of 32.
|
||||||
"""
|
"""
|
||||||
w, h = Img.w/2, Img.h/2
|
w, h = Img.w/2, Img.h/2
|
||||||
dx, dy = int(-(-(w-1)//16)), int(-(-(h-1)//12))
|
dx, dy = int(-(-(w-1)//grid_w)), int(-(-(h-1)//grid_h))
|
||||||
"""
|
"""
|
||||||
average the green channels into one
|
average the green channels into one
|
||||||
"""
|
"""
|
||||||
av_ch_g = np.mean((channels[1:3]), axis=0)
|
av_ch_g = np.mean((channels[1:3]), axis=0)
|
||||||
if do_alsc_colour:
|
if do_alsc_colour:
|
||||||
"""
|
"""
|
||||||
obtain 16x12 grid of intensities for each channel and subtract black level
|
obtain grid_w x grid_h grid of intensities for each channel and subtract black level
|
||||||
"""
|
"""
|
||||||
g = get_16x12_grid(av_ch_g, dx, dy) - Img.blacklevel_16
|
g = get_grid(av_ch_g, dx, dy, grid_size) - Img.blacklevel_16
|
||||||
r = get_16x12_grid(channels[0], dx, dy) - Img.blacklevel_16
|
r = get_grid(channels[0], dx, dy, grid_size) - Img.blacklevel_16
|
||||||
b = get_16x12_grid(channels[3], dx, dy) - Img.blacklevel_16
|
b = get_grid(channels[3], dx, dy, grid_size) - Img.blacklevel_16
|
||||||
"""
|
"""
|
||||||
calculate ratios as 32 bit in order to be supported by medianBlur function
|
calculate ratios as 32 bit in order to be supported by medianBlur function
|
||||||
"""
|
"""
|
||||||
cr = np.reshape(g/r, (12, 16)).astype('float32')
|
cr = np.reshape(g/r, (grid_h, grid_w)).astype('float32')
|
||||||
cb = np.reshape(g/b, (12, 16)).astype('float32')
|
cb = np.reshape(g/b, (grid_h, grid_w)).astype('float32')
|
||||||
cg = np.reshape(1/g, (12, 16)).astype('float32')
|
cg = np.reshape(1/g, (grid_h, grid_w)).astype('float32')
|
||||||
"""
|
"""
|
||||||
median blur to remove peaks and save as float 64
|
median blur to remove peaks and save as float 64
|
||||||
"""
|
"""
|
||||||
|
@ -164,7 +167,7 @@ def alsc(Cam, Img, do_alsc_colour, plot=False):
|
||||||
"""
|
"""
|
||||||
note Y is plotted as -Y so plot has same axes as image
|
note Y is plotted as -Y so plot has same axes as image
|
||||||
"""
|
"""
|
||||||
X, Y = np.meshgrid(range(16), range(12))
|
X, Y = np.meshgrid(range(grid_w), range(grid_h))
|
||||||
ha.plot_surface(X, -Y, cr, cmap=cm.coolwarm, linewidth=0)
|
ha.plot_surface(X, -Y, cr, cmap=cm.coolwarm, linewidth=0)
|
||||||
ha.set_title('ALSC Plot\nImg: {}\n\ncr'.format(Img.str))
|
ha.set_title('ALSC Plot\nImg: {}\n\ncr'.format(Img.str))
|
||||||
hb = hf.add_subplot(312, projection='3d')
|
hb = hf.add_subplot(312, projection='3d')
|
||||||
|
@ -182,15 +185,15 @@ def alsc(Cam, Img, do_alsc_colour, plot=False):
|
||||||
"""
|
"""
|
||||||
only perform calculations for luminance shading
|
only perform calculations for luminance shading
|
||||||
"""
|
"""
|
||||||
g = get_16x12_grid(av_ch_g, dx, dy) - Img.blacklevel_16
|
g = get_grid(av_ch_g, dx, dy, grid_size) - Img.blacklevel_16
|
||||||
cg = np.reshape(1/g, (12, 16)).astype('float32')
|
cg = np.reshape(1/g, (grid_h, grid_w)).astype('float32')
|
||||||
cg = cv2.medianBlur(cg, 3).astype('float64')
|
cg = cv2.medianBlur(cg, 3).astype('float64')
|
||||||
cg = cg/np.min(cg)
|
cg = cg/np.min(cg)
|
||||||
|
|
||||||
if plot:
|
if plot:
|
||||||
hf = plt.figure(figssize=(8, 8))
|
hf = plt.figure(figssize=(8, 8))
|
||||||
ha = hf.add_subplot(1, 1, 1, projection='3d')
|
ha = hf.add_subplot(1, 1, 1, projection='3d')
|
||||||
X, Y = np.meashgrid(range(16), range(12))
|
X, Y = np.meashgrid(range(grid_w), range(grid_h))
|
||||||
ha.plot_surface(X, -Y, cg, cmap=cm.coolwarm, linewidth=0)
|
ha.plot_surface(X, -Y, cg, cmap=cm.coolwarm, linewidth=0)
|
||||||
ha.set_title('ALSC Plot (Luminance only!)\nImg: {}\n\ncg').format(Img.str)
|
ha.set_title('ALSC Plot (Luminance only!)\nImg: {}\n\ncg').format(Img.str)
|
||||||
plt.show()
|
plt.show()
|
||||||
|
@ -199,21 +202,22 @@ def alsc(Cam, Img, do_alsc_colour, plot=False):
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Compresses channel down to a 16x12 grid
|
Compresses channel down to a grid of the requested size
|
||||||
"""
|
"""
|
||||||
def get_16x12_grid(chan, dx, dy):
|
def get_grid(chan, dx, dy, grid_size):
|
||||||
|
grid_w, grid_h = grid_size
|
||||||
grid = []
|
grid = []
|
||||||
"""
|
"""
|
||||||
since left and bottom border will not necessarily have rectangles of
|
since left and bottom border will not necessarily have rectangles of
|
||||||
dimension dx x dy, the 32nd iteration has to be handled separately.
|
dimension dx x dy, the 32nd iteration has to be handled separately.
|
||||||
"""
|
"""
|
||||||
for i in range(11):
|
for i in range(grid_h - 1):
|
||||||
for j in range(15):
|
for j in range(grid_w - 1):
|
||||||
grid.append(np.mean(chan[dy*i:dy*(1+i), dx*j:dx*(1+j)]))
|
grid.append(np.mean(chan[dy*i:dy*(1+i), dx*j:dx*(1+j)]))
|
||||||
grid.append(np.mean(chan[dy*i:dy*(1+i), 15*dx:]))
|
grid.append(np.mean(chan[dy*i:dy*(1+i), (grid_w - 1)*dx:]))
|
||||||
for j in range(15):
|
for j in range(grid_w - 1):
|
||||||
grid.append(np.mean(chan[11*dy:, dx*j:dx*(1+j)]))
|
grid.append(np.mean(chan[(grid_h - 1)*dy:, dx*j:dx*(1+j)]))
|
||||||
grid.append(np.mean(chan[11*dy:, 15*dx:]))
|
grid.append(np.mean(chan[(grid_h - 1)*dy:, (grid_w - 1)*dx:]))
|
||||||
"""
|
"""
|
||||||
return as np.array, ready for further manipulation
|
return as np.array, ready for further manipulation
|
||||||
"""
|
"""
|
||||||
|
@ -223,7 +227,7 @@ def get_16x12_grid(chan, dx, dy):
|
||||||
"""
|
"""
|
||||||
obtains sigmas for red and blue, effectively a measure of the 'error'
|
obtains sigmas for red and blue, effectively a measure of the 'error'
|
||||||
"""
|
"""
|
||||||
def get_sigma(Cam, cal_cr_list, cal_cb_list):
|
def get_sigma(Cam, cal_cr_list, cal_cb_list, grid_size):
|
||||||
Cam.log += '\nCalculating sigmas'
|
Cam.log += '\nCalculating sigmas'
|
||||||
"""
|
"""
|
||||||
provided colour alsc tables were generated for two different colour
|
provided colour alsc tables were generated for two different colour
|
||||||
|
@ -241,8 +245,8 @@ def get_sigma(Cam, cal_cr_list, cal_cb_list):
|
||||||
sigma_rs = []
|
sigma_rs = []
|
||||||
sigma_bs = []
|
sigma_bs = []
|
||||||
for i in range(len(cts)-1):
|
for i in range(len(cts)-1):
|
||||||
sigma_rs.append(calc_sigma(cal_cr_list[i]['table'], cal_cr_list[i+1]['table']))
|
sigma_rs.append(calc_sigma(cal_cr_list[i]['table'], cal_cr_list[i+1]['table'], grid_size))
|
||||||
sigma_bs.append(calc_sigma(cal_cb_list[i]['table'], cal_cb_list[i+1]['table']))
|
sigma_bs.append(calc_sigma(cal_cb_list[i]['table'], cal_cb_list[i+1]['table'], grid_size))
|
||||||
Cam.log += '\nColour temperature interval {} - {} K'.format(cts[i], cts[i+1])
|
Cam.log += '\nColour temperature interval {} - {} K'.format(cts[i], cts[i+1])
|
||||||
Cam.log += '\nSigma red: {}'.format(sigma_rs[-1])
|
Cam.log += '\nSigma red: {}'.format(sigma_rs[-1])
|
||||||
Cam.log += '\nSigma blue: {}'.format(sigma_bs[-1])
|
Cam.log += '\nSigma blue: {}'.format(sigma_bs[-1])
|
||||||
|
@ -263,12 +267,13 @@ def get_sigma(Cam, cal_cr_list, cal_cb_list):
|
||||||
"""
|
"""
|
||||||
calculate sigma from two adjacent gain tables
|
calculate sigma from two adjacent gain tables
|
||||||
"""
|
"""
|
||||||
def calc_sigma(g1, g2):
|
def calc_sigma(g1, g2, grid_size):
|
||||||
|
grid_w, grid_h = grid_size
|
||||||
"""
|
"""
|
||||||
reshape into 16x12 matrix
|
reshape into 16x12 matrix
|
||||||
"""
|
"""
|
||||||
g1 = np.reshape(g1, (12, 16))
|
g1 = np.reshape(g1, (grid_h, grid_w))
|
||||||
g2 = np.reshape(g2, (12, 16))
|
g2 = np.reshape(g2, (grid_h, grid_w))
|
||||||
"""
|
"""
|
||||||
apply gains to gain table
|
apply gains to gain table
|
||||||
"""
|
"""
|
||||||
|
@ -280,8 +285,8 @@ def calc_sigma(g1, g2):
|
||||||
neighbours, then append to list
|
neighbours, then append to list
|
||||||
"""
|
"""
|
||||||
diffs = []
|
diffs = []
|
||||||
for i in range(10):
|
for i in range(grid_h - 2):
|
||||||
for j in range(14):
|
for j in range(grid_w - 2):
|
||||||
"""
|
"""
|
||||||
note indexing is incremented by 1 since all patches on borders are
|
note indexing is incremented by 1 since all patches on borders are
|
||||||
not counted
|
not counted
|
||||||
|
|
|
@ -13,7 +13,7 @@ from scipy.optimize import fmin
|
||||||
"""
|
"""
|
||||||
obtain piecewise linear approximation for colour curve
|
obtain piecewise linear approximation for colour curve
|
||||||
"""
|
"""
|
||||||
def awb(Cam, cal_cr_list, cal_cb_list, plot):
|
def awb(Cam, cal_cr_list, cal_cb_list, plot, grid_size):
|
||||||
imgs = Cam.imgs
|
imgs = Cam.imgs
|
||||||
"""
|
"""
|
||||||
condense alsc calibration tables into one dictionary
|
condense alsc calibration tables into one dictionary
|
||||||
|
@ -43,7 +43,7 @@ def awb(Cam, cal_cr_list, cal_cb_list, plot):
|
||||||
Note: if alsc is disabled then colour_cals will be set to None and the
|
Note: if alsc is disabled then colour_cals will be set to None and the
|
||||||
function will just return the greyscale patches
|
function will just return the greyscale patches
|
||||||
"""
|
"""
|
||||||
r_patchs, b_patchs, g_patchs = get_alsc_patches(Img, colour_cals)
|
r_patchs, b_patchs, g_patchs = get_alsc_patches(Img, colour_cals, grid_size=grid_size)
|
||||||
"""
|
"""
|
||||||
calculate ratio of r, b to g
|
calculate ratio of r, b to g
|
||||||
"""
|
"""
|
||||||
|
@ -293,12 +293,13 @@ def awb(Cam, cal_cr_list, cal_cb_list, plot):
|
||||||
"""
|
"""
|
||||||
obtain greyscale patches and perform alsc colour correction
|
obtain greyscale patches and perform alsc colour correction
|
||||||
"""
|
"""
|
||||||
def get_alsc_patches(Img, colour_cals, grey=True):
|
def get_alsc_patches(Img, colour_cals, grey=True, grid_size=(16, 12)):
|
||||||
"""
|
"""
|
||||||
get patch centre coordinates, image colour and the actual
|
get patch centre coordinates, image colour and the actual
|
||||||
patches for each channel, remembering to subtract blacklevel
|
patches for each channel, remembering to subtract blacklevel
|
||||||
If grey then only greyscale patches considered
|
If grey then only greyscale patches considered
|
||||||
"""
|
"""
|
||||||
|
grid_w, grid_h = grid_size
|
||||||
if grey:
|
if grey:
|
||||||
cen_coords = Img.cen_coords[3::4]
|
cen_coords = Img.cen_coords[3::4]
|
||||||
col = Img.col
|
col = Img.col
|
||||||
|
@ -345,12 +346,12 @@ def get_alsc_patches(Img, colour_cals, grey=True):
|
||||||
bef_tabs = np.array(colour_cals[bef])
|
bef_tabs = np.array(colour_cals[bef])
|
||||||
aft_tabs = np.array(colour_cals[aft])
|
aft_tabs = np.array(colour_cals[aft])
|
||||||
col_tabs = (bef_tabs*db + aft_tabs*da)/(da+db)
|
col_tabs = (bef_tabs*db + aft_tabs*da)/(da+db)
|
||||||
col_tabs = np.reshape(col_tabs, (2, 12, 16))
|
col_tabs = np.reshape(col_tabs, (2, grid_h, grid_w))
|
||||||
"""
|
"""
|
||||||
calculate dx, dy used to calculate alsc table
|
calculate dx, dy used to calculate alsc table
|
||||||
"""
|
"""
|
||||||
w, h = Img.w/2, Img.h/2
|
w, h = Img.w/2, Img.h/2
|
||||||
dx, dy = int(-(-(w-1)//16)), int(-(-(h-1)//12))
|
dx, dy = int(-(-(w-1)//grid_w)), int(-(-(h-1)//grid_h))
|
||||||
"""
|
"""
|
||||||
make list of pairs of gains for each patch by selecting the correct value
|
make list of pairs of gains for each patch by selecting the correct value
|
||||||
in alsc colour calibration table
|
in alsc colour calibration table
|
||||||
|
|
|
@ -56,7 +56,7 @@ FInds colour correction matrices for list of images
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def ccm(Cam, cal_cr_list, cal_cb_list):
|
def ccm(Cam, cal_cr_list, cal_cb_list, grid_size):
|
||||||
global matrix_selection_types, typenum
|
global matrix_selection_types, typenum
|
||||||
imgs = Cam.imgs
|
imgs = Cam.imgs
|
||||||
"""
|
"""
|
||||||
|
@ -133,9 +133,7 @@ def ccm(Cam, cal_cr_list, cal_cb_list):
|
||||||
Note: if alsc is disabled then colour_cals will be set to None and no
|
Note: if alsc is disabled then colour_cals will be set to None and no
|
||||||
the function will simply return the macbeth patches
|
the function will simply return the macbeth patches
|
||||||
"""
|
"""
|
||||||
r, b, g = get_alsc_patches(Img, colour_cals, grey=False)
|
r, b, g = get_alsc_patches(Img, colour_cals, grey=False, grid_size=grid_size)
|
||||||
# 256 values for each patch of sRGB values
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
do awb
|
do awb
|
||||||
Note: awb is done by measuring the macbeth chart in the image, rather
|
Note: awb is done by measuring the macbeth chart in the image, rather
|
||||||
|
|
233
utils/raspberrypi/ctt/ctt_pisp.py
Executable file
233
utils/raspberrypi/ctt/ctt_pisp.py
Executable file
|
@ -0,0 +1,233 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
#
|
||||||
|
# Copyright (C) 2019, Raspberry Pi Ltd
|
||||||
|
#
|
||||||
|
# ctt_pisp.py - camera tuning tool for PiSP platforms
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from ctt_run import run_ctt
|
||||||
|
from ctt_tools import parse_input
|
||||||
|
|
||||||
|
json_template = {
|
||||||
|
"rpi.black_level": {
|
||||||
|
"black_level": 4096
|
||||||
|
},
|
||||||
|
"rpi.lux": {
|
||||||
|
"reference_shutter_speed": 10000,
|
||||||
|
"reference_gain": 1,
|
||||||
|
"reference_aperture": 1.0
|
||||||
|
},
|
||||||
|
"rpi.dpc": {
|
||||||
|
"strength": 1
|
||||||
|
},
|
||||||
|
"rpi.noise": {
|
||||||
|
},
|
||||||
|
"rpi.geq": {
|
||||||
|
},
|
||||||
|
"rpi.denoise":
|
||||||
|
{
|
||||||
|
"sdn":
|
||||||
|
{
|
||||||
|
"deviation": 1.6,
|
||||||
|
"strength": 0.5,
|
||||||
|
"deviation2": 3.2,
|
||||||
|
"deviation_no_tdn": 3.2,
|
||||||
|
"strength_no_tdn": 0.75
|
||||||
|
},
|
||||||
|
"cdn":
|
||||||
|
{
|
||||||
|
"deviation": 200,
|
||||||
|
"strength": 0.3
|
||||||
|
},
|
||||||
|
"tdn":
|
||||||
|
{
|
||||||
|
"deviation": 0.8,
|
||||||
|
"threshold": 0.05
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rpi.awb": {
|
||||||
|
"priors": [
|
||||||
|
{"lux": 0, "prior": [2000, 1.0, 3000, 0.0, 13000, 0.0]},
|
||||||
|
{"lux": 800, "prior": [2000, 0.0, 6000, 2.0, 13000, 2.0]},
|
||||||
|
{"lux": 1500, "prior": [2000, 0.0, 4000, 1.0, 6000, 6.0, 6500, 7.0, 7000, 1.0, 13000, 1.0]}
|
||||||
|
],
|
||||||
|
"modes": {
|
||||||
|
"auto": {"lo": 2500, "hi": 7700},
|
||||||
|
"incandescent": {"lo": 2500, "hi": 3000},
|
||||||
|
"tungsten": {"lo": 3000, "hi": 3500},
|
||||||
|
"fluorescent": {"lo": 4000, "hi": 4700},
|
||||||
|
"indoor": {"lo": 3000, "hi": 5000},
|
||||||
|
"daylight": {"lo": 5500, "hi": 6500},
|
||||||
|
"cloudy": {"lo": 7000, "hi": 8000}
|
||||||
|
},
|
||||||
|
"bayes": 1
|
||||||
|
},
|
||||||
|
"rpi.agc": {
|
||||||
|
"metering_modes": {
|
||||||
|
"centre-weighted": {
|
||||||
|
"weights": [
|
||||||
|
0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
|
||||||
|
0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
|
||||||
|
1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
|
||||||
|
1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
|
||||||
|
1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
|
||||||
|
1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
|
||||||
|
1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
|
||||||
|
1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
|
||||||
|
1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
|
||||||
|
1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
|
||||||
|
1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
|
||||||
|
1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
|
||||||
|
1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
|
||||||
|
0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
|
||||||
|
0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"spot": {
|
||||||
|
"weights": [
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"matrix": {
|
||||||
|
"weights": [
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"exposure_modes": {
|
||||||
|
"normal": {
|
||||||
|
"shutter": [100, 10000, 30000, 60000, 66666],
|
||||||
|
"gain": [1.0, 1.5, 2.0, 4.0, 8.0]
|
||||||
|
},
|
||||||
|
"short": {
|
||||||
|
"shutter": [100, 5000, 10000, 20000, 60000],
|
||||||
|
"gain": [1.0, 1.5, 2.0, 4.0, 8.0]
|
||||||
|
},
|
||||||
|
"long":
|
||||||
|
{
|
||||||
|
"shutter": [ 100, 10000, 30000, 60000, 90000, 120000 ],
|
||||||
|
"gain": [ 1.0, 1.5, 2.0, 4.0, 8.0, 12.0 ]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"constraint_modes": {
|
||||||
|
"normal": [
|
||||||
|
{"bound": "LOWER", "q_lo": 0.98, "q_hi": 1.0, "y_target": [0, 0.5, 1000, 0.5]}
|
||||||
|
],
|
||||||
|
"highlight": [
|
||||||
|
{"bound": "LOWER", "q_lo": 0.98, "q_hi": 1.0, "y_target": [0, 0.5, 1000, 0.5]},
|
||||||
|
{"bound": "UPPER", "q_lo": 0.98, "q_hi": 1.0, "y_target": [0, 0.8, 1000, 0.8]}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"y_target": [0, 0.16, 1000, 0.165, 10000, 0.17]
|
||||||
|
},
|
||||||
|
"rpi.alsc": {
|
||||||
|
'omega': 1.3,
|
||||||
|
'n_iter': 100,
|
||||||
|
'luminance_strength': 0.8,
|
||||||
|
},
|
||||||
|
"rpi.contrast": {
|
||||||
|
"ce_enable": 1,
|
||||||
|
"gamma_curve": [
|
||||||
|
0, 0,
|
||||||
|
1024, 5040,
|
||||||
|
2048, 9338,
|
||||||
|
3072, 12356,
|
||||||
|
4096, 15312,
|
||||||
|
5120, 18051,
|
||||||
|
6144, 20790,
|
||||||
|
7168, 23193,
|
||||||
|
8192, 25744,
|
||||||
|
9216, 27942,
|
||||||
|
10240, 30035,
|
||||||
|
11264, 32005,
|
||||||
|
12288, 33975,
|
||||||
|
13312, 35815,
|
||||||
|
14336, 37600,
|
||||||
|
15360, 39168,
|
||||||
|
16384, 40642,
|
||||||
|
18432, 43379,
|
||||||
|
20480, 45749,
|
||||||
|
22528, 47753,
|
||||||
|
24576, 49621,
|
||||||
|
26624, 51253,
|
||||||
|
28672, 52698,
|
||||||
|
30720, 53796,
|
||||||
|
32768, 54876,
|
||||||
|
36864, 57012,
|
||||||
|
40960, 58656,
|
||||||
|
45056, 59954,
|
||||||
|
49152, 61183,
|
||||||
|
53248, 62355,
|
||||||
|
57344, 63419,
|
||||||
|
61440, 64476,
|
||||||
|
65535, 65535
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"rpi.ccm": {
|
||||||
|
},
|
||||||
|
"rpi.sharpen": {
|
||||||
|
"threshold": 0.25,
|
||||||
|
"limit": 1.0,
|
||||||
|
"strength": 1.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
grid_size = (32, 32)
|
||||||
|
|
||||||
|
target = 'pisp'
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
"""
|
||||||
|
initialise calibration
|
||||||
|
"""
|
||||||
|
if len(sys.argv) == 1:
|
||||||
|
print("""
|
||||||
|
PiSP Camera Tuning Tool version 1.0
|
||||||
|
|
||||||
|
Required Arguments:
|
||||||
|
'-i' : Calibration image directory.
|
||||||
|
'-o' : Name of output json file.
|
||||||
|
|
||||||
|
Optional Arguments:
|
||||||
|
'-c' : Config file for the CTT. If not passed, default parameters used.
|
||||||
|
'-l' : Name of output log file. If not passed, 'ctt_log.txt' used.
|
||||||
|
""")
|
||||||
|
quit(0)
|
||||||
|
else:
|
||||||
|
"""
|
||||||
|
parse input arguments
|
||||||
|
"""
|
||||||
|
json_output, directory, config, log_output = parse_input()
|
||||||
|
run_ctt(json_output, directory, config, log_output, json_template, grid_size, target)
|
|
@ -19,6 +19,7 @@ class Encoder(json.JSONEncoder):
|
||||||
self.indentation_level = 0
|
self.indentation_level = 0
|
||||||
self.hard_break = 120
|
self.hard_break = 120
|
||||||
self.custom_elems = {
|
self.custom_elems = {
|
||||||
|
'weights': 15,
|
||||||
'table': 16,
|
'table': 16,
|
||||||
'luminance_lut': 16,
|
'luminance_lut': 16,
|
||||||
'ct_curve': 3,
|
'ct_curve': 3,
|
||||||
|
@ -87,7 +88,7 @@ class Encoder(json.JSONEncoder):
|
||||||
return self.encode(o)
|
return self.encode(o)
|
||||||
|
|
||||||
|
|
||||||
def pretty_print(in_json: dict) -> str:
|
def pretty_print(in_json: dict, custom_elems={}) -> str:
|
||||||
|
|
||||||
if 'version' not in in_json or \
|
if 'version' not in in_json or \
|
||||||
'target' not in in_json or \
|
'target' not in in_json or \
|
||||||
|
@ -95,7 +96,9 @@ def pretty_print(in_json: dict) -> str:
|
||||||
in_json['version'] < 2.0:
|
in_json['version'] < 2.0:
|
||||||
raise RuntimeError('Incompatible JSON dictionary has been provided')
|
raise RuntimeError('Incompatible JSON dictionary has been provided')
|
||||||
|
|
||||||
return json.dumps(in_json, cls=Encoder, indent=4, sort_keys=False)
|
encoder = Encoder(indent=4, sort_keys=False)
|
||||||
|
encoder.custom_elems |= custom_elems
|
||||||
|
return encoder.encode(in_json) #json.dumps(in_json, cls=Encoder, indent=4, sort_keys=False)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
@ -67,7 +67,7 @@ Camera object that is the backbone of the tuning tool.
|
||||||
Input is the desired path of the output json.
|
Input is the desired path of the output json.
|
||||||
"""
|
"""
|
||||||
class Camera:
|
class Camera:
|
||||||
def __init__(self, jfile):
|
def __init__(self, jfile, json):
|
||||||
self.path = os.path.dirname(os.path.expanduser(__file__)) + '/'
|
self.path = os.path.dirname(os.path.expanduser(__file__)) + '/'
|
||||||
if self.path == '/':
|
if self.path == '/':
|
||||||
self.path = ''
|
self.path = ''
|
||||||
|
@ -79,127 +79,15 @@ class Camera:
|
||||||
"""
|
"""
|
||||||
initial json dict populated by uncalibrated values
|
initial json dict populated by uncalibrated values
|
||||||
"""
|
"""
|
||||||
self.json = {
|
|
||||||
"rpi.black_level": {
|
self.json = json
|
||||||
"black_level": 4096
|
|
||||||
},
|
|
||||||
"rpi.dpc": {
|
|
||||||
},
|
|
||||||
"rpi.lux": {
|
|
||||||
"reference_shutter_speed": 10000,
|
|
||||||
"reference_gain": 1,
|
|
||||||
"reference_aperture": 1.0
|
|
||||||
},
|
|
||||||
"rpi.noise": {
|
|
||||||
},
|
|
||||||
"rpi.geq": {
|
|
||||||
},
|
|
||||||
"rpi.sdn": {
|
|
||||||
},
|
|
||||||
"rpi.awb": {
|
|
||||||
"priors": [
|
|
||||||
{"lux": 0, "prior": [2000, 1.0, 3000, 0.0, 13000, 0.0]},
|
|
||||||
{"lux": 800, "prior": [2000, 0.0, 6000, 2.0, 13000, 2.0]},
|
|
||||||
{"lux": 1500, "prior": [2000, 0.0, 4000, 1.0, 6000, 6.0, 6500, 7.0, 7000, 1.0, 13000, 1.0]}
|
|
||||||
],
|
|
||||||
"modes": {
|
|
||||||
"auto": {"lo": 2500, "hi": 8000},
|
|
||||||
"incandescent": {"lo": 2500, "hi": 3000},
|
|
||||||
"tungsten": {"lo": 3000, "hi": 3500},
|
|
||||||
"fluorescent": {"lo": 4000, "hi": 4700},
|
|
||||||
"indoor": {"lo": 3000, "hi": 5000},
|
|
||||||
"daylight": {"lo": 5500, "hi": 6500},
|
|
||||||
"cloudy": {"lo": 7000, "hi": 8600}
|
|
||||||
},
|
|
||||||
"bayes": 1
|
|
||||||
},
|
|
||||||
"rpi.agc": {
|
|
||||||
"metering_modes": {
|
|
||||||
"centre-weighted": {
|
|
||||||
"weights": [3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0]
|
|
||||||
},
|
|
||||||
"spot": {
|
|
||||||
"weights": [2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
|
||||||
},
|
|
||||||
"matrix": {
|
|
||||||
"weights": [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"exposure_modes": {
|
|
||||||
"normal": {
|
|
||||||
"shutter": [100, 10000, 30000, 60000, 120000],
|
|
||||||
"gain": [1.0, 2.0, 4.0, 6.0, 6.0]
|
|
||||||
},
|
|
||||||
"short": {
|
|
||||||
"shutter": [100, 5000, 10000, 20000, 120000],
|
|
||||||
"gain": [1.0, 2.0, 4.0, 6.0, 6.0]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"constraint_modes": {
|
|
||||||
"normal": [
|
|
||||||
{"bound": "LOWER", "q_lo": 0.98, "q_hi": 1.0, "y_target": [0, 0.5, 1000, 0.5]}
|
|
||||||
],
|
|
||||||
"highlight": [
|
|
||||||
{"bound": "LOWER", "q_lo": 0.98, "q_hi": 1.0, "y_target": [0, 0.5, 1000, 0.5]},
|
|
||||||
{"bound": "UPPER", "q_lo": 0.98, "q_hi": 1.0, "y_target": [0, 0.8, 1000, 0.8]}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"y_target": [0, 0.16, 1000, 0.165, 10000, 0.17]
|
|
||||||
},
|
|
||||||
"rpi.alsc": {
|
|
||||||
'omega': 1.3,
|
|
||||||
'n_iter': 100,
|
|
||||||
'luminance_strength': 0.7,
|
|
||||||
},
|
|
||||||
"rpi.contrast": {
|
|
||||||
"ce_enable": 1,
|
|
||||||
"gamma_curve": [
|
|
||||||
0, 0,
|
|
||||||
1024, 5040,
|
|
||||||
2048, 9338,
|
|
||||||
3072, 12356,
|
|
||||||
4096, 15312,
|
|
||||||
5120, 18051,
|
|
||||||
6144, 20790,
|
|
||||||
7168, 23193,
|
|
||||||
8192, 25744,
|
|
||||||
9216, 27942,
|
|
||||||
10240, 30035,
|
|
||||||
11264, 32005,
|
|
||||||
12288, 33975,
|
|
||||||
13312, 35815,
|
|
||||||
14336, 37600,
|
|
||||||
15360, 39168,
|
|
||||||
16384, 40642,
|
|
||||||
18432, 43379,
|
|
||||||
20480, 45749,
|
|
||||||
22528, 47753,
|
|
||||||
24576, 49621,
|
|
||||||
26624, 51253,
|
|
||||||
28672, 52698,
|
|
||||||
30720, 53796,
|
|
||||||
32768, 54876,
|
|
||||||
36864, 57012,
|
|
||||||
40960, 58656,
|
|
||||||
45056, 59954,
|
|
||||||
49152, 61183,
|
|
||||||
53248, 62355,
|
|
||||||
57344, 63419,
|
|
||||||
61440, 64476,
|
|
||||||
65535, 65535
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"rpi.ccm": {
|
|
||||||
},
|
|
||||||
"rpi.sharpen": {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Perform colour correction calibrations by comparing macbeth patch colours
|
Perform colour correction calibrations by comparing macbeth patch colours
|
||||||
to standard macbeth chart colours.
|
to standard macbeth chart colours.
|
||||||
"""
|
"""
|
||||||
def ccm_cal(self, do_alsc_colour):
|
def ccm_cal(self, do_alsc_colour, grid_size):
|
||||||
if 'rpi.ccm' in self.disable:
|
if 'rpi.ccm' in self.disable:
|
||||||
return 1
|
return 1
|
||||||
print('\nStarting CCM calibration')
|
print('\nStarting CCM calibration')
|
||||||
|
@ -245,7 +133,7 @@ class Camera:
|
||||||
Do CCM calibration
|
Do CCM calibration
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
ccms = ccm(self, cal_cr_list, cal_cb_list)
|
ccms = ccm(self, cal_cr_list, cal_cb_list, grid_size)
|
||||||
except ArithmeticError:
|
except ArithmeticError:
|
||||||
print('ERROR: Matrix is singular!\nTake new pictures and try again...')
|
print('ERROR: Matrix is singular!\nTake new pictures and try again...')
|
||||||
self.log += '\nERROR: Singular matrix encountered during fit!'
|
self.log += '\nERROR: Singular matrix encountered during fit!'
|
||||||
|
@ -263,7 +151,7 @@ class Camera:
|
||||||
various colour temperatures, as well as providing a maximum 'wiggle room'
|
various colour temperatures, as well as providing a maximum 'wiggle room'
|
||||||
distance from this curve (transverse_neg/pos).
|
distance from this curve (transverse_neg/pos).
|
||||||
"""
|
"""
|
||||||
def awb_cal(self, greyworld, do_alsc_colour):
|
def awb_cal(self, greyworld, do_alsc_colour, grid_size):
|
||||||
if 'rpi.awb' in self.disable:
|
if 'rpi.awb' in self.disable:
|
||||||
return 1
|
return 1
|
||||||
print('\nStarting AWB calibration')
|
print('\nStarting AWB calibration')
|
||||||
|
@ -306,7 +194,7 @@ class Camera:
|
||||||
call calibration function
|
call calibration function
|
||||||
"""
|
"""
|
||||||
plot = "rpi.awb" in self.plot
|
plot = "rpi.awb" in self.plot
|
||||||
awb_out = awb(self, cal_cr_list, cal_cb_list, plot)
|
awb_out = awb(self, cal_cr_list, cal_cb_list, plot, grid_size)
|
||||||
ct_curve, transverse_neg, transverse_pos = awb_out
|
ct_curve, transverse_neg, transverse_pos = awb_out
|
||||||
"""
|
"""
|
||||||
write output to json
|
write output to json
|
||||||
|
@ -324,7 +212,7 @@ class Camera:
|
||||||
colour channel seperately, and then partially corrects for vignetting.
|
colour channel seperately, and then partially corrects for vignetting.
|
||||||
The extent of the correction depends on the 'luminance_strength' parameter.
|
The extent of the correction depends on the 'luminance_strength' parameter.
|
||||||
"""
|
"""
|
||||||
def alsc_cal(self, luminance_strength, do_alsc_colour):
|
def alsc_cal(self, luminance_strength, do_alsc_colour, grid_size):
|
||||||
if 'rpi.alsc' in self.disable:
|
if 'rpi.alsc' in self.disable:
|
||||||
return 1
|
return 1
|
||||||
print('\nStarting ALSC calibration')
|
print('\nStarting ALSC calibration')
|
||||||
|
@ -347,7 +235,7 @@ class Camera:
|
||||||
call calibration function
|
call calibration function
|
||||||
"""
|
"""
|
||||||
plot = "rpi.alsc" in self.plot
|
plot = "rpi.alsc" in self.plot
|
||||||
alsc_out = alsc_all(self, do_alsc_colour, plot)
|
alsc_out = alsc_all(self, do_alsc_colour, plot, grid_size)
|
||||||
cal_cr_list, cal_cb_list, luminance_lut, av_corn = alsc_out
|
cal_cr_list, cal_cb_list, luminance_lut, av_corn = alsc_out
|
||||||
"""
|
"""
|
||||||
write output to json and finish if not do_alsc_colour
|
write output to json and finish if not do_alsc_colour
|
||||||
|
@ -393,7 +281,7 @@ class Camera:
|
||||||
"""
|
"""
|
||||||
obtain worst-case scenario residual sigmas
|
obtain worst-case scenario residual sigmas
|
||||||
"""
|
"""
|
||||||
sigma_r, sigma_b = get_sigma(self, cal_cr_list, cal_cb_list)
|
sigma_r, sigma_b = get_sigma(self, cal_cr_list, cal_cb_list, grid_size)
|
||||||
"""
|
"""
|
||||||
write output to json
|
write output to json
|
||||||
"""
|
"""
|
||||||
|
@ -509,19 +397,20 @@ class Camera:
|
||||||
"""
|
"""
|
||||||
writes the json dictionary to the raw json file then make pretty
|
writes the json dictionary to the raw json file then make pretty
|
||||||
"""
|
"""
|
||||||
def write_json(self):
|
def write_json(self, version=2.0, target='bcm2835', grid_size=(16, 12)):
|
||||||
"""
|
"""
|
||||||
Write json dictionary to file using our version 2 format
|
Write json dictionary to file using our version 2 format
|
||||||
"""
|
"""
|
||||||
|
|
||||||
out_json = {
|
out_json = {
|
||||||
"version": 2.0,
|
"version": version,
|
||||||
'target': 'bcm2835',
|
'target': target if target != 'vc4' else 'bcm2835',
|
||||||
"algorithms": [{name: data} for name, data in self.json.items()],
|
"algorithms": [{name: data} for name, data in self.json.items()],
|
||||||
}
|
}
|
||||||
|
|
||||||
with open(self.jf, 'w') as f:
|
with open(self.jf, 'w') as f:
|
||||||
f.write(pretty_print(out_json))
|
f.write(pretty_print(out_json,
|
||||||
|
custom_elems={'table': grid_size[0], 'luminance_lut': grid_size[0]}))
|
||||||
|
|
||||||
"""
|
"""
|
||||||
add a new section to the log file
|
add a new section to the log file
|
||||||
|
@ -712,7 +601,7 @@ class Camera:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
def run_ctt(json_output, directory, config, log_output, alsc_only=False):
|
def run_ctt(json_output, directory, config, log_output, json_template, grid_size, target, alsc_only=False):
|
||||||
"""
|
"""
|
||||||
check input files are jsons
|
check input files are jsons
|
||||||
"""
|
"""
|
||||||
|
@ -748,7 +637,7 @@ def run_ctt(json_output, directory, config, log_output, alsc_only=False):
|
||||||
greyworld = get_config(awb_d, "greyworld", 0, 'bool')
|
greyworld = get_config(awb_d, "greyworld", 0, 'bool')
|
||||||
alsc_d = get_config(configs, "alsc", {}, 'dict')
|
alsc_d = get_config(configs, "alsc", {}, 'dict')
|
||||||
do_alsc_colour = get_config(alsc_d, "do_alsc_colour", 1, 'bool')
|
do_alsc_colour = get_config(alsc_d, "do_alsc_colour", 1, 'bool')
|
||||||
luminance_strength = get_config(alsc_d, "luminance_strength", 0.5, 'num')
|
luminance_strength = get_config(alsc_d, "luminance_strength", 0.8, 'num')
|
||||||
blacklevel = get_config(configs, "blacklevel", -1, 'num')
|
blacklevel = get_config(configs, "blacklevel", -1, 'num')
|
||||||
macbeth_d = get_config(configs, "macbeth", {}, 'dict')
|
macbeth_d = get_config(configs, "macbeth", {}, 'dict')
|
||||||
mac_small = get_config(macbeth_d, "small", 0, 'bool')
|
mac_small = get_config(macbeth_d, "small", 0, 'bool')
|
||||||
|
@ -772,7 +661,7 @@ def run_ctt(json_output, directory, config, log_output, alsc_only=False):
|
||||||
initialise tuning tool and load images
|
initialise tuning tool and load images
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
Cam = Camera(json_output)
|
Cam = Camera(json_output, json=json_template)
|
||||||
Cam.log_user_input(json_output, directory, config, log_output)
|
Cam.log_user_input(json_output, directory, config, log_output)
|
||||||
if alsc_only:
|
if alsc_only:
|
||||||
disable = set(Cam.json.keys()).symmetric_difference({"rpi.alsc"})
|
disable = set(Cam.json.keys()).symmetric_difference({"rpi.alsc"})
|
||||||
|
@ -794,14 +683,16 @@ def run_ctt(json_output, directory, config, log_output, alsc_only=False):
|
||||||
Cam.json['rpi.black_level']['black_level'] = Cam.blacklevel_16
|
Cam.json['rpi.black_level']['black_level'] = Cam.blacklevel_16
|
||||||
Cam.json_remove(disable)
|
Cam.json_remove(disable)
|
||||||
print('\nSTARTING CALIBRATIONS')
|
print('\nSTARTING CALIBRATIONS')
|
||||||
Cam.alsc_cal(luminance_strength, do_alsc_colour)
|
Cam.alsc_cal(luminance_strength, do_alsc_colour, grid_size)
|
||||||
Cam.geq_cal()
|
Cam.geq_cal()
|
||||||
Cam.lux_cal()
|
Cam.lux_cal()
|
||||||
Cam.noise_cal()
|
Cam.noise_cal()
|
||||||
Cam.awb_cal(greyworld, do_alsc_colour)
|
Cam.cac_cal(do_alsc_colour)
|
||||||
Cam.ccm_cal(do_alsc_colour)
|
Cam.awb_cal(greyworld, do_alsc_colour, grid_size)
|
||||||
|
Cam.ccm_cal(do_alsc_colour, grid_size)
|
||||||
|
|
||||||
print('\nFINISHED CALIBRATIONS')
|
print('\nFINISHED CALIBRATIONS')
|
||||||
Cam.write_json()
|
Cam.write_json(target=target, grid_size=grid_size)
|
||||||
Cam.write_log(log_output)
|
Cam.write_log(log_output)
|
||||||
print('\nCalibrations written to: '+json_output)
|
print('\nCalibrations written to: '+json_output)
|
||||||
if log_output is None:
|
if log_output is None:
|
||||||
|
@ -810,28 +701,3 @@ def run_ctt(json_output, directory, config, log_output, alsc_only=False):
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
Cam.write_log(log_output)
|
Cam.write_log(log_output)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
"""
|
|
||||||
initialise calibration
|
|
||||||
"""
|
|
||||||
if len(sys.argv) == 1:
|
|
||||||
print("""
|
|
||||||
Pisp Camera Tuning Tool version 1.0
|
|
||||||
|
|
||||||
Required Arguments:
|
|
||||||
'-i' : Calibration image directory.
|
|
||||||
'-o' : Name of output json file.
|
|
||||||
|
|
||||||
Optional Arguments:
|
|
||||||
'-c' : Config file for the CTT. If not passed, default parameters used.
|
|
||||||
'-l' : Name of output log file. If not passed, 'ctt_log.txt' used.
|
|
||||||
""")
|
|
||||||
quit(0)
|
|
||||||
else:
|
|
||||||
"""
|
|
||||||
parse input arguments
|
|
||||||
"""
|
|
||||||
json_output, directory, config, log_output = parse_input()
|
|
||||||
run_ctt(json_output, directory, config, log_output)
|
|
157
utils/raspberrypi/ctt/ctt_vc4.py
Executable file
157
utils/raspberrypi/ctt/ctt_vc4.py
Executable file
|
@ -0,0 +1,157 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
#
|
||||||
|
# Copyright (C) 2019, Raspberry Pi Ltd
|
||||||
|
#
|
||||||
|
# ctt_vc4.py - camera tuning tool for VC4 platforms
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from ctt_run import run_ctt
|
||||||
|
from ctt_tools import parse_input
|
||||||
|
|
||||||
|
json_template = {
|
||||||
|
"rpi.black_level": {
|
||||||
|
"black_level": 4096
|
||||||
|
},
|
||||||
|
"rpi.dpc": {
|
||||||
|
},
|
||||||
|
"rpi.lux": {
|
||||||
|
"reference_shutter_speed": 10000,
|
||||||
|
"reference_gain": 1,
|
||||||
|
"reference_aperture": 1.0
|
||||||
|
},
|
||||||
|
"rpi.noise": {
|
||||||
|
},
|
||||||
|
"rpi.geq": {
|
||||||
|
},
|
||||||
|
"rpi.sdn": {
|
||||||
|
},
|
||||||
|
"rpi.awb": {
|
||||||
|
"priors": [
|
||||||
|
{"lux": 0, "prior": [2000, 1.0, 3000, 0.0, 13000, 0.0]},
|
||||||
|
{"lux": 800, "prior": [2000, 0.0, 6000, 2.0, 13000, 2.0]},
|
||||||
|
{"lux": 1500, "prior": [2000, 0.0, 4000, 1.0, 6000, 6.0, 6500, 7.0, 7000, 1.0, 13000, 1.0]}
|
||||||
|
],
|
||||||
|
"modes": {
|
||||||
|
"auto": {"lo": 2500, "hi": 8000},
|
||||||
|
"incandescent": {"lo": 2500, "hi": 3000},
|
||||||
|
"tungsten": {"lo": 3000, "hi": 3500},
|
||||||
|
"fluorescent": {"lo": 4000, "hi": 4700},
|
||||||
|
"indoor": {"lo": 3000, "hi": 5000},
|
||||||
|
"daylight": {"lo": 5500, "hi": 6500},
|
||||||
|
"cloudy": {"lo": 7000, "hi": 8600}
|
||||||
|
},
|
||||||
|
"bayes": 1
|
||||||
|
},
|
||||||
|
"rpi.agc": {
|
||||||
|
"metering_modes": {
|
||||||
|
"centre-weighted": {
|
||||||
|
"weights": [3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0]
|
||||||
|
},
|
||||||
|
"spot": {
|
||||||
|
"weights": [2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||||
|
},
|
||||||
|
"matrix": {
|
||||||
|
"weights": [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"exposure_modes": {
|
||||||
|
"normal": {
|
||||||
|
"shutter": [100, 10000, 30000, 60000, 120000],
|
||||||
|
"gain": [1.0, 2.0, 4.0, 6.0, 6.0]
|
||||||
|
},
|
||||||
|
"short": {
|
||||||
|
"shutter": [100, 5000, 10000, 20000, 120000],
|
||||||
|
"gain": [1.0, 2.0, 4.0, 6.0, 6.0]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"constraint_modes": {
|
||||||
|
"normal": [
|
||||||
|
{"bound": "LOWER", "q_lo": 0.98, "q_hi": 1.0, "y_target": [0, 0.5, 1000, 0.5]}
|
||||||
|
],
|
||||||
|
"highlight": [
|
||||||
|
{"bound": "LOWER", "q_lo": 0.98, "q_hi": 1.0, "y_target": [0, 0.5, 1000, 0.5]},
|
||||||
|
{"bound": "UPPER", "q_lo": 0.98, "q_hi": 1.0, "y_target": [0, 0.8, 1000, 0.8]}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"y_target": [0, 0.16, 1000, 0.165, 10000, 0.17]
|
||||||
|
},
|
||||||
|
"rpi.alsc": {
|
||||||
|
'omega': 1.3,
|
||||||
|
'n_iter': 100,
|
||||||
|
'luminance_strength': 0.7,
|
||||||
|
},
|
||||||
|
"rpi.contrast": {
|
||||||
|
"ce_enable": 1,
|
||||||
|
"gamma_curve": [
|
||||||
|
0, 0,
|
||||||
|
1024, 5040,
|
||||||
|
2048, 9338,
|
||||||
|
3072, 12356,
|
||||||
|
4096, 15312,
|
||||||
|
5120, 18051,
|
||||||
|
6144, 20790,
|
||||||
|
7168, 23193,
|
||||||
|
8192, 25744,
|
||||||
|
9216, 27942,
|
||||||
|
10240, 30035,
|
||||||
|
11264, 32005,
|
||||||
|
12288, 33975,
|
||||||
|
13312, 35815,
|
||||||
|
14336, 37600,
|
||||||
|
15360, 39168,
|
||||||
|
16384, 40642,
|
||||||
|
18432, 43379,
|
||||||
|
20480, 45749,
|
||||||
|
22528, 47753,
|
||||||
|
24576, 49621,
|
||||||
|
26624, 51253,
|
||||||
|
28672, 52698,
|
||||||
|
30720, 53796,
|
||||||
|
32768, 54876,
|
||||||
|
36864, 57012,
|
||||||
|
40960, 58656,
|
||||||
|
45056, 59954,
|
||||||
|
49152, 61183,
|
||||||
|
53248, 62355,
|
||||||
|
57344, 63419,
|
||||||
|
61440, 64476,
|
||||||
|
65535, 65535
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"rpi.ccm": {
|
||||||
|
},
|
||||||
|
"rpi.sharpen": {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
grid_size = (16, 12)
|
||||||
|
|
||||||
|
target = 'bcm2835'
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
"""
|
||||||
|
initialise calibration
|
||||||
|
"""
|
||||||
|
if len(sys.argv) == 1:
|
||||||
|
print("""
|
||||||
|
VC4 Camera Tuning Tool version 1.0
|
||||||
|
|
||||||
|
Required Arguments:
|
||||||
|
'-i' : Calibration image directory.
|
||||||
|
'-o' : Name of output json file.
|
||||||
|
|
||||||
|
Optional Arguments:
|
||||||
|
'-c' : Config file for the CTT. If not passed, default parameters used.
|
||||||
|
'-l' : Name of output log file. If not passed, 'ctt_log.txt' used.
|
||||||
|
""")
|
||||||
|
quit(0)
|
||||||
|
else:
|
||||||
|
"""
|
||||||
|
parse input arguments
|
||||||
|
"""
|
||||||
|
json_output, directory, config, log_output = parse_input()
|
||||||
|
run_ctt(json_output, directory, config, log_output, json_template, grid_size, target)
|
Loading…
Add table
Add a link
Reference in a new issue