i hope this message does not look too pedantic, i just want to present what i
learned in the last weeks when trying to convert DCPs to movs.
this all stems from problems i've seen with real life dcp, but i can not share
material from those dcps, that's why i created an artificial image to
demonstrate the same problem. i could see similar effects with the dcps i handled.
preparation: generate a dcp with dcp-o-matic
generate a test image as 16bit png:
it shows a color gradient in 1024 steps from black to white.
below three other gradients are generated from black to dark and from black to
even darker. only the first black to white gradient is used to generate the
graphs below, the other gradients are provided for visual inspection.
Code: Select all
from wand.image import Image
from wand.drawing import Drawing
from wand.color import Color
with Drawing() as draw:
c = 0
for x in range(0, 1025):
c = x * 2**6
if x == 1024:
c = 2**16 - 1
print(f"{c:0>4x}")
for y in range(0, 100):
draw.fill_color = Color(f"#{c:0>4x}{c:0>4x}{c:0>4x}")
draw.point(x, y)
for y_offset, m in enumerate([8, 4, 2, 1], 1):
for x in range(0, 1025):
for y in range(100):
c = x * m
draw.fill_color = Color(f"#{c:0>4x}{c:0>4x}{c:0>4x}")
draw.point(x, y + y_offset * 100)
with Image(width=1998, height=1080, depth=64, background=Color("gray50")) as image:
draw(image)
image.format = "png"
image.save(filename="PNG64:generated.png")
load this generated png into dcp-o-matic and generate a dcp
generate a ProRes mov from this DCP using dcp-o-matic
load the dcp into dcp-o-matic and generate a prores mov from it.
when i open this generated mov with mpv, the additional black to gray gradients
already show artifacts clearly visible on my not calibrated monitor.
to further inspect the gradient i'm extracting a single frame from the video using ffmpeg:
Code: Select all
ffmpeg -i *.mov -vf "select=eq(n\,4)" -vframes 1 frame-4.png
this png is then inspected using another python script
Code: Select all
import argparse
from wand.image import Image
from wand.display import display
def load(filename):
with Image(filename=filename) as img:
for x in range(1025):
c = img[x, 0]
# red, green, blue are float values between 0 and 1 so we have to
# convert them back to the 16 bit values
gray = ((c.red + c.green + c.blue) / 3) * (2**16 - 1)
print("{}, {:10.2f}".format(x * 2**6, gray))
def main():
parser = argparse.ArgumentParser()
parser.add_argument("filename")
args = parser.parse_args()
load(args.filename)
if __name__ == "__main__":
main()
Code: Select all
#!/usr/bin/gnuplot
reset
set border 3 back
set tics nomirror 8192
set terminal pngcairo size 1920, 1080
set output 'plot.png'
set object 1 rectangle from screen 0,0 to screen 1,1 fillcolor rgb "#ffffff" behind
set style line 1 linecolor rgb '#0060ad' linetype 1 linewidth 2 pointtype 7 pointsize 0
set rmargin 10
plot '../final/dom-frame-4.csv' title 'dom' at end with line linecolor rgb '#006600' linewidth 1, \
'../final/generated.csv' title '' at end with line linecolor rgb '#000000' linewidth 1
generated.png image used as input for the DCP)
this shows that there are some modifications to the dark areas of the image,
and that there are artifacts in the curve. the pattern is in line of what i can
see on my monitor.
generate a ProRes mov from the DCP using ffmpeg and libplacebo
you need to use a very new libplacebo to see the same results, older versions
had a bug that show a blue tint. i'm using libplacebo compiled from git commit
2bd627f823ba1cedbc51a0ee6eb7a9fb433d912e for this demo.
Code: Select all
ffmpeg -i j2c_df390784-f932-437c-a7af-018d3cf21a25.mxf -vframes 1 -filter_complex "select=eq(n\,4),libplacebo=format=yuv444p10le:colorspace=bt709:color_primaries=bt709:color_trc=bt709:range=pc" -y libplacebo-frame-4.png
and the graph shows that the line of the gradient is closer to the original,
although some small artifacts are still visible.
conclusion
we converted a 16 bit sRGB image into the XYZ color space for the DCP and then
convert it back to yuv444p12 and then back to a png. as far as i understand
this could work perfectly for the concrete example, as we know that we only
expect sRGB values in the XYZ color space in the DCP, but for the general
solution the ProRes/YUV can not reproduce the colors stored in DCP/XYZ, so some
color distortion is expected. therefor i'm kind of impressed by the result of
libplacebo.
from the visual appearance with my (not calibrated) monitor i would prefer the
color adaption used by libplacebo, also the generated graphs are in favour of
libplacebo.
maybe vanilla ffmpeg can also be tuned in a way that it provides similar
results as libplacebo, but i stopped looking when i was satisfied with the
result.
maybe this post can help improve dcp-o-matic, or it can improve my
understanding of colorspaces, ffmpeg and dcps. please let me know if there is a
flaw in my analysis.
all the best,
benedikt