summaryrefslogtreecommitdiff
path: root/utils/rkisp1
diff options
context:
space:
mode:
Diffstat (limited to 'utils/rkisp1')
-rwxr-xr-xutils/rkisp1/gen-csc-table.py215
-rwxr-xr-xutils/rkisp1/rkisp1-capture.sh64
2 files changed, 274 insertions, 5 deletions
diff --git a/utils/rkisp1/gen-csc-table.py b/utils/rkisp1/gen-csc-table.py
new file mode 100755
index 00000000..ffc0370a
--- /dev/null
+++ b/utils/rkisp1/gen-csc-table.py
@@ -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))
diff --git a/utils/rkisp1/rkisp1-capture.sh b/utils/rkisp1/rkisp1-capture.sh
index 4d09f5d5..d767e31d 100755
--- a/utils/rkisp1/rkisp1-capture.sh
+++ b/utils/rkisp1/rkisp1-capture.sh
@@ -4,8 +4,7 @@
#
# Author: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
#
-# rkisp-capture.sh - Capture processed frames from cameras based on the
-# Rockchip ISP1
+# 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:
@@ -14,6 +13,37 @@
# - 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
@@ -28,6 +58,17 @@ find_sensor() {
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
@@ -51,7 +92,7 @@ get_sensor_format() {
local format
local sensor=$1
- format=$($mediactl --get-v4l2 "'$sensor':0" | sed 's/\[\([^ ]*\).*/\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/')
@@ -63,15 +104,27 @@ 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
- $mediactl -l "'$sensor':0 -> 'rkisp1_isp':0 [1]"
+ 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]"
@@ -88,6 +141,7 @@ capture_frames() {
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 \
@@ -170,7 +224,7 @@ mediactl="media-ctl -d $mdev"
get_sensor_format "$sensor"
if [[ $raw == true ]] ; then
- capture_format=$(echo $sensor_mbus_code | sed 's/_[0-9X]$//')
+ capture_format=$(echo $sensor_mbus_code | sed 's/_[0-9X]*$//')
capture_mbus_code=$sensor_mbus_code
else
capture_format=YUYV