An attempt at getting image data back
This commit is contained in:
215
spider-cam/libcamera/utils/rkisp1/gen-csc-table.py
Executable file
215
spider-cam/libcamera/utils/rkisp1/gen-csc-table.py
Executable file
@@ -0,0 +1,215 @@
|
||||
#!/usr/bin/env python3
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
# Copyright (C) 2022, Ideas on Board Oy
|
||||
#
|
||||
# Generate color space conversion table coefficients with configurable
|
||||
# fixed-point precision
|
||||
|
||||
import argparse
|
||||
import enum
|
||||
import numpy as np
|
||||
import sys
|
||||
|
||||
|
||||
encodings = {
|
||||
'rec601': [
|
||||
[ 0.299, 0.587, 0.114 ],
|
||||
[ -0.299 / 1.772, -0.587 / 1.772, 0.886 / 1.772 ],
|
||||
[ 0.701 / 1.402, -0.587 / 1.402, -0.114 / 1.402 ]
|
||||
],
|
||||
'rec709': [
|
||||
[ 0.2126, 0.7152, 0.0722 ],
|
||||
[ -0.2126 / 1.8556, -0.7152 / 1.8556, 0.9278 / 1.8556 ],
|
||||
[ 0.7874 / 1.5748, -0.7152 / 1.5748, -0.0722 / 1.5748 ]
|
||||
],
|
||||
'rec2020': [
|
||||
[ 0.2627, 0.6780, 0.0593 ],
|
||||
[ -0.2627 / 1.8814, -0.6780 / 1.8814, 0.9407 / 1.8814 ],
|
||||
[ 0.7373 / 1.4746, -0.6780 / 1.4746, -0.0593 / 1.4746 ],
|
||||
],
|
||||
'smpte240m': [
|
||||
[ 0.2122, 0.7013, 0.0865 ],
|
||||
[ -0.2122 / 1.8270, -0.7013 / 1.8270, 0.9135 / 1.8270 ],
|
||||
[ 0.7878 / 1.5756, -0.7013 / 1.5756, -0.0865 / 1.5756 ],
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
class Precision(object):
|
||||
def __init__(self, precision):
|
||||
if precision[0].upper() != 'Q':
|
||||
raise RuntimeError(f'Invalid precision `{precision}`')
|
||||
prec = precision[1:].split('.')
|
||||
if len(prec) != 2:
|
||||
raise RuntimeError(f'Invalid precision `{precision}`')
|
||||
|
||||
self.__prec = [int(v) for v in prec]
|
||||
|
||||
@property
|
||||
def integer(self):
|
||||
return self.__prec[0]
|
||||
|
||||
@property
|
||||
def fractional(self):
|
||||
return self.__prec[1]
|
||||
|
||||
@property
|
||||
def total(self):
|
||||
# Add 1 for the sign bit
|
||||
return self.__prec[0] + self.__prec[1] + 1
|
||||
|
||||
|
||||
class Quantization(enum.Enum):
|
||||
FULL = 0
|
||||
LIMITED = 1
|
||||
|
||||
|
||||
def scale_coeff(coeff, quantization, luma):
|
||||
"""Scale a coefficient to the output range dictated by the quantization.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
coeff : float
|
||||
The CSC matrix coefficient to scale
|
||||
quantization : Quantization
|
||||
The quantization, either FULL or LIMITED
|
||||
luma : bool
|
||||
True if the coefficient corresponds to a luma value, False otherwise
|
||||
"""
|
||||
|
||||
# Assume the input range is 8 bits. The output range is set by the
|
||||
# quantization and differs between luma and chrome components for limited
|
||||
# range.
|
||||
in_range = 255 - 0
|
||||
if quantization == Quantization.FULL:
|
||||
out_range = 255 - 0
|
||||
elif luma:
|
||||
out_range = 235 - 16
|
||||
else:
|
||||
out_range = 240 - 16
|
||||
|
||||
return coeff * out_range / in_range
|
||||
|
||||
|
||||
def round_array(values):
|
||||
"""Round a list of signed floating point values to the closest integer while
|
||||
preserving the (rounded) value of the sum of all elements.
|
||||
"""
|
||||
|
||||
# Calculate the rounding error as the difference between the rounded sum of
|
||||
# values and the sum of rounded values. This is by definition an integer
|
||||
# (positive or negative), which indicates how many values will need to be
|
||||
# 'flipped' to the opposite rounding.
|
||||
rounded_values = [round(value) for value in values]
|
||||
sum_values = round(sum(values))
|
||||
sum_error = sum_values - sum(rounded_values)
|
||||
|
||||
if sum_error == 0:
|
||||
return rounded_values
|
||||
|
||||
# The next step is to distribute the error among the values, in a way that
|
||||
# will minimize the relative error introduced in individual values. We
|
||||
# extend the values list with the rounded value and original index for each
|
||||
# element, and sort by rounding error. Then we modify the elements with the
|
||||
# highest or lowest error, depending on whether the sum error is negative
|
||||
# or positive.
|
||||
|
||||
values = [[value, round(value), index] for index, value in enumerate(values)]
|
||||
values.sort(key=lambda v: v[1] - v[0])
|
||||
|
||||
# It could also be argued that the key for the sort order should not be the
|
||||
# absolute rouding error but the relative error, as the impact of identical
|
||||
# rounding errors will differ for coefficients with widely different values.
|
||||
# This is a topic for further research.
|
||||
#
|
||||
# values.sort(key=lambda v: (v[1] - v[0]) / abs(v[0]))
|
||||
|
||||
if sum_error > 0:
|
||||
for i in range(sum_error):
|
||||
values[i][1] += 1
|
||||
else:
|
||||
for i in range(-sum_error):
|
||||
values[len(values) - i - 1][1] -= 1
|
||||
|
||||
# Finally, sort back by index, make sure the total rounding error is now 0,
|
||||
# and return the rounded values.
|
||||
values.sort(key=lambda v: v[2])
|
||||
values = [value[1] for value in values]
|
||||
assert(sum(values) == sum_values)
|
||||
|
||||
return values
|
||||
|
||||
|
||||
def main(argv):
|
||||
|
||||
# Parse command line arguments.
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Generate color space conversion table coefficients with '
|
||||
'configurable fixed-point precision.'
|
||||
)
|
||||
parser.add_argument('--invert', '-i', action='store_true',
|
||||
help='Invert the color space conversion (YUV -> RGB)')
|
||||
parser.add_argument('--precision', '-p', default='Q1.7',
|
||||
help='The output fixed point precision in Q notation (sign bit excluded)')
|
||||
parser.add_argument('--quantization', '-q', choices=['full', 'limited'],
|
||||
default='limited', help='Quantization range')
|
||||
parser.add_argument('encoding', choices=encodings.keys(), help='YCbCr encoding')
|
||||
args = parser.parse_args(argv[1:])
|
||||
|
||||
try:
|
||||
precision = Precision(args.precision)
|
||||
except Exception:
|
||||
print(f'Invalid precision `{args.precision}`')
|
||||
return 1
|
||||
|
||||
encoding = encodings[args.encoding]
|
||||
quantization = Quantization[args.quantization.upper()]
|
||||
|
||||
# Scale and round the encoding coefficients based on the precision and
|
||||
# quantization range.
|
||||
luma = True
|
||||
scaled_coeffs = []
|
||||
for line in encoding:
|
||||
line = [scale_coeff(coeff, quantization, luma) for coeff in line]
|
||||
scaled_coeffs.append(line)
|
||||
luma = False
|
||||
|
||||
if args.invert:
|
||||
scaled_coeffs = np.linalg.inv(scaled_coeffs)
|
||||
|
||||
rounded_coeffs = []
|
||||
for line in scaled_coeffs:
|
||||
line = [coeff * (1 << precision.fractional) for coeff in line]
|
||||
# For the RGB to YUV conversion, use a rounding method that preserves
|
||||
# the rounded sum of each line to avoid biases and overflow, as the sum
|
||||
# of luma and chroma coefficients should be 1.0 and 0.0 respectively
|
||||
# (in full range). For the YUV to RGB conversion, there is no such
|
||||
# constraint, so use simple rounding.
|
||||
if args.invert:
|
||||
line = [round(coeff) for coeff in line]
|
||||
else:
|
||||
line = round_array(line)
|
||||
|
||||
# Convert coefficients to the number of bits selected by the precision.
|
||||
# Negative values will be turned into positive integers using 2's
|
||||
# complement.
|
||||
line = [coeff & ((1 << precision.total) - 1) for coeff in line]
|
||||
rounded_coeffs.append(line)
|
||||
|
||||
# Print the result as C code.
|
||||
nbits = 1 << (precision.total - 1).bit_length()
|
||||
nbytes = nbits // 4
|
||||
print(f'static const u{nbits} {"yuv2rgb" if args.invert else "rgb2yuv"}_{args.encoding}_{quantization.name.lower()}_coeffs[] = {{')
|
||||
|
||||
for line in rounded_coeffs:
|
||||
line = [f'0x{coeff:0{nbytes}x}' for coeff in line]
|
||||
|
||||
print(f'\t{", ".join(line)},')
|
||||
|
||||
print('};')
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main(sys.argv))
|
||||
236
spider-cam/libcamera/utils/rkisp1/rkisp1-capture.sh
Executable file
236
spider-cam/libcamera/utils/rkisp1/rkisp1-capture.sh
Executable file
@@ -0,0 +1,236 @@
|
||||
#!/bin/sh
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
# Copyright (C) 2019, Google Inc.
|
||||
#
|
||||
# Author: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
|
||||
#
|
||||
# Capture processed frames from cameras based on the Rockchip ISP1
|
||||
#
|
||||
# The scripts makes use of the following tools, which are expected to be
|
||||
# executable from the system-wide path or from the local directory:
|
||||
#
|
||||
# - media-ctl (from v4l-utils git://linuxtv.org/v4l-utils.git)
|
||||
# - raw2rgbpnm (from git://git.retiisi.org.uk/~sailus/raw2rgbpnm.git)
|
||||
# - yavta (from git://git.ideasonboard.org/yavta.git)
|
||||
|
||||
# Return the entity connected to a given pad
|
||||
# $1: The pad, expressed as "entity":index
|
||||
mc_remote_entity() {
|
||||
local entity="${1%:*}"
|
||||
local pad="${1#*:}"
|
||||
|
||||
${mediactl} -p | awk '
|
||||
/^- entity / {
|
||||
in_entity=0
|
||||
}
|
||||
|
||||
/^- entity [0-9]+: '"${entity}"' / {
|
||||
in_entity=1
|
||||
}
|
||||
|
||||
/^[ \t]+pad/ {
|
||||
in_pad=0
|
||||
}
|
||||
|
||||
/^[ \t]+pad'"${pad}"': / {
|
||||
in_pad=1
|
||||
}
|
||||
|
||||
/^[ \t]+(<-|->) "[^"]+"/ {
|
||||
if (in_entity && in_pad) {
|
||||
print gensub(/^[^"]+"([^"]+)":([0-9]+).*$/, "\\1", "g")
|
||||
exit
|
||||
}
|
||||
}'
|
||||
}
|
||||
|
||||
# Locate the sensor entity
|
||||
find_sensor() {
|
||||
local bus
|
||||
local sensor_name=$1
|
||||
|
||||
bus=$(grep "$sensor_name " /sys/class/video4linux/v4l-subdev*/name | cut -d ' ' -f 2)
|
||||
if [[ -z $bus ]]; then
|
||||
echo "Sensor '$sensor_name' not found." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "$sensor_name $bus"
|
||||
}
|
||||
|
||||
# Locate the CSI-2 receiver
|
||||
find_csi2_rx() {
|
||||
local sensor_name=$1
|
||||
local csi2_rx
|
||||
|
||||
csi2_rx=$(mc_remote_entity "$sensor_name:0")
|
||||
if [ "$csi2_rx" != rkisp1_isp ] ; then
|
||||
echo "$csi2_rx"
|
||||
fi
|
||||
}
|
||||
|
||||
# Locate the media device
|
||||
find_media_device() {
|
||||
local mdev
|
||||
local name=$1
|
||||
|
||||
for mdev in /dev/media* ; do
|
||||
media-ctl -d $mdev -p | grep -q "^driver[ \t]*$name$" && break
|
||||
mdev=
|
||||
done
|
||||
|
||||
if [[ -z $mdev ]] ; then
|
||||
echo "$name media device not found." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo $mdev
|
||||
}
|
||||
|
||||
# Get the sensor format
|
||||
get_sensor_format() {
|
||||
local format
|
||||
local sensor=$1
|
||||
|
||||
format=$($mediactl --get-v4l2 "'$sensor':0" | grep 'fmt:' | sed 's/.*\(fmt:\S*\).*/\1/')
|
||||
sensor_mbus_code=$(echo $format | sed 's/fmt:\([A-Z0-9_]*\).*/\1/')
|
||||
sensor_size=$(echo $format | sed 's/[^\/]*\/\([0-9x]*\).*/\1/')
|
||||
|
||||
echo "Capturing ${sensor_size} from sensor $sensor in ${sensor_mbus_code}"
|
||||
}
|
||||
|
||||
# Configure the pipeline
|
||||
configure_pipeline() {
|
||||
local format="fmt:$sensor_mbus_code/$sensor_size"
|
||||
local capture_mbus_code=$1
|
||||
local capture_size=$2
|
||||
local csi2_rx
|
||||
|
||||
echo "Configuring pipeline for $sensor in $format"
|
||||
|
||||
csi2_rx=$(find_csi2_rx "$sensor")
|
||||
|
||||
$mediactl -r
|
||||
|
||||
if [ -n "$csi2_rx" ] ; then
|
||||
$mediactl -l "'$sensor':0 -> '$csi2_rx':0 [1]"
|
||||
$mediactl -l "'$csi2_rx':1 -> 'rkisp1_isp':0 [1]"
|
||||
else
|
||||
$mediactl -l "'$sensor':0 -> 'rkisp1_isp':0 [1]"
|
||||
fi
|
||||
$mediactl -l "'rkisp1_isp':2 -> 'rkisp1_resizer_mainpath':0 [1]"
|
||||
|
||||
$mediactl -V "\"$sensor\":0 [$format]"
|
||||
if [ -n "$csi2_rx" ] ; then
|
||||
$mediactl -V "'$csi2_rx':0 [$format]"
|
||||
$mediactl -V "'$csi2_rx':1 [$format]"
|
||||
fi
|
||||
$mediactl -V "'rkisp1_isp':0 [$format crop:(0,0)/$sensor_size]"
|
||||
$mediactl -V "'rkisp1_isp':2 [fmt:$capture_mbus_code/$sensor_size crop:(0,0)/$sensor_size]"
|
||||
$mediactl -V "'rkisp1_resizer_mainpath':0 [fmt:$capture_mbus_code/$sensor_size crop:(0,0)/$sensor_size]"
|
||||
$mediactl -V "'rkisp1_resizer_mainpath':1 [fmt:$capture_mbus_code/$capture_size]"
|
||||
}
|
||||
|
||||
# Capture frames
|
||||
capture_frames() {
|
||||
local file_op
|
||||
local capture_format=$1
|
||||
local capture_size=$2
|
||||
local frame_count=$3
|
||||
local save_file=$4
|
||||
|
||||
if [[ $save_file -eq 1 ]]; then
|
||||
file_op="--file=/tmp/frame-#.bin"
|
||||
rm -f /tmp/frame-*.bin
|
||||
fi
|
||||
|
||||
yavta -c$frame_count -n5 -I -f $capture_format -s $capture_size \
|
||||
$file_op $($mediactl -e "rkisp1_mainpath")
|
||||
}
|
||||
|
||||
# Convert captured files to ppm
|
||||
convert_files() {
|
||||
local format=$1
|
||||
local size=$2
|
||||
local frame_count=$3
|
||||
|
||||
echo "Converting ${frame_count} frames (${size})"
|
||||
|
||||
for i in `seq 0 $(($frame_count - 1))`; do
|
||||
i=$(printf %06u $i)
|
||||
raw2rgbpnm -f $format -s $size /tmp/frame-$i.bin /tmp/frame-$i.ppm
|
||||
done
|
||||
}
|
||||
|
||||
# Print usage message
|
||||
usage() {
|
||||
echo "Usage: $1 [options] sensor-name"
|
||||
echo "Supported options:"
|
||||
echo "-c,--count n Number of frame to capture"
|
||||
echo "--no-save Do not save captured frames to disk"
|
||||
echo "-r, --raw Capture RAW frames"
|
||||
echo "-s, --size wxh Frame size"
|
||||
}
|
||||
|
||||
# Parse command line arguments
|
||||
capture_size=1024x768
|
||||
frame_count=10
|
||||
raw=false
|
||||
save_file=1
|
||||
|
||||
while [[ $# -ne 0 ]] ; do
|
||||
case $1 in
|
||||
-c|--count)
|
||||
frame_count=$2
|
||||
shift 2
|
||||
;;
|
||||
--no-save)
|
||||
save_file=0
|
||||
shift
|
||||
;;
|
||||
|
||||
-r|--raw)
|
||||
raw=true
|
||||
shift
|
||||
;;
|
||||
-s|--size)
|
||||
capture_size=$2
|
||||
shift 2
|
||||
;;
|
||||
-*)
|
||||
echo "Unsupported option $1" >&2
|
||||
usage $0
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ $# -ne 1 ]] ; then
|
||||
usage $0
|
||||
exit 1
|
||||
fi
|
||||
|
||||
sensor_name=$1
|
||||
|
||||
modprobe phy_rockchip_dphy_rx0
|
||||
modprobe rockchip_isp1
|
||||
|
||||
sensor=$(find_sensor $sensor_name) || exit
|
||||
mdev=$(find_media_device rkisp1) || exit
|
||||
mediactl="media-ctl -d $mdev"
|
||||
|
||||
get_sensor_format "$sensor"
|
||||
if [[ $raw == true ]] ; then
|
||||
capture_format=$(echo $sensor_mbus_code | sed 's/_[0-9X]*$//')
|
||||
capture_mbus_code=$sensor_mbus_code
|
||||
else
|
||||
capture_format=YUYV
|
||||
capture_mbus_code=YUYV8_2X8
|
||||
fi
|
||||
|
||||
configure_pipeline $capture_mbus_code $capture_size
|
||||
capture_frames $capture_format $capture_size $frame_count $save_file
|
||||
[[ $save_file -eq 1 ]] && convert_files $capture_format $capture_size $frame_count
|
||||
Reference in New Issue
Block a user