Show AI denoise in the manual on sharp, noisy frames

The ISO 8000 Brooklyn Bridge frame was soft at 1:1 before any denoising,
so it could not show that Best keeps detail. The section now shows five
sharp frames from ISO 4000 to 16000, each at 1:1 where hard edges meet
flat areas, Bilinear beside Best: a subway poster, a cathedral portal, a
circus clock, a clinic sign and graffiti. Fast is described, not shown.

at_develop opens a DNG when the name carries its extension. The scene
now fails when Best never lands instead of capturing the undenoised
frame twice.
This commit is contained in:
2026-10-08 22:22:40 -04:00
parent fc3b1ca440
commit f813014e80
16 changed files with 98 additions and 56 deletions
+47 -36
View File
@@ -63,7 +63,6 @@ ALPS_SKY = '_MG_8330' # an upright alpine frame, its top third sky and clo
TOWER = '_MG_8693' # towers shot looking up: verticals that converge
ROAD = '_MG_8672' # a road under a sky, with marks on it: masks and repair
ROAD_MARK = (0.305, 0.794) # a dark mark on its tarmac
DENOISE = '_MG_8862' # Brooklyn Bridge at ISO 8000: a lit railing, a lamp and a camera against the night
# --- the registry -----------------------------------------------------------
SCENES = {}
@@ -259,14 +258,17 @@ def cell_width():
def at_develop(photo=CHINATOWN):
"""`photo` open in develop, on the Photo tool, the All group, the
column at its top, and the edit as it was opened."""
if dr.present('‹ Library@Button') and dr.present(f'{photo}.CR2'):
column at its top, and the edit as it was opened. A name without an
extension is a CR2."""
stem, ext = os.path.splitext(photo)
name = photo if ext else f'{photo}.CR2'
if dr.present('‹ Library@Button') and dr.present(name):
pass
else:
at_library()
grid_top()
dr.click(*cell(photo))
dr.wait_for(f'{photo}.CR2', 30)
dr.click(*cell(stem))
dr.wait_for(name, 30)
wait_canvas()
for done in ('Done Composing', 'Done Masking', 'Done Repairing'):
if dr.present(f'{done}@Button'):
@@ -842,43 +844,52 @@ def develop_zoom():
pause(1.2)
# The methods in the order the scene visits them: `Best` is what the
# photograph opens with, then each smaller network, then none.
DENOISE_METHODS = ['Best', 'Fast', 'Bilinear']
DENOISE_CLOSE_UP = 560 # pixels of canvas, square, around the lamp at 1:1
# Sharp, noisy frames, each with a point on it at 1:1 where hard edges meet
# flat areas — where a denoiser either keeps the edge or smears it: a slug
# for its pictures, the file, and the point as fractions of the photograph.
DENOISE_FRAMES = [
('poster', '_MG_8953', (0.40, 0.31)), # ISO 4000: a subway poster's masthead, lines on paper
('portal', '_MG_4762', (0.18, 0.22)), # ISO 8000: carving round a cathedral door, smooth stone
('clock', '_MG_4810', (0.47, 0.39)), # ISO 12800: a circus clock's numerals on flat canvas
('sign', 'IMG_2515.dng', (0.66, 0.52)), # ISO 16000: lettering and a swoosh on a white panel
('graffiti', 'IMG_2041.dng', (0.695, 0.655)), # ISO 16000: ink lines and dots on a white wall
]
DENOISE_CLOSE_UP = 480 # pixels of canvas, square, around the point at 1:1
@scene(media=[f'develop-denoise-{m.lower()}.png' for m in DENOISE_METHODS],
@scene(media=[f'develop-denoise-{slug}-{m}.png' for slug, _, _ in DENOISE_FRAMES
for m in ('bilinear', 'best')],
sources=DEVELOP_SRC + ['ui/dr-ui/src/develop/denoise.rs', 'core/dr-denoise/**',
'core/dr-pipeline/src/learned_denoise.rs', 'models/denoise/**'])
def develop_denoise():
"""The lit lamp and railing of an ISO 8000 night frame at 1:1, once per
AI Denoise method, each cut to the same square of the canvas. Waits for
each network rather than for a fixed time: on the CPU, which the demo
profile uses, Best takes several times what Fast does."""
mark = log_size()
at_develop(DENOISE)
a = dr.photo(0.45, 0.55) # the lit lamp, the railing and the skyline over the water
dr.move(*a)
pause(0.3)
dr.x('click', '--repeat', 2, '--delay', 80, 1)
pause(1.5)
group('Detail')
in_column('AI Denoise@Text')
for method in DENOISE_METHODS:
if method != 'Best':
mark = log_size()
dr.click(*in_column(f'{method}@RadioButton'))
if method != 'Bilinear':
t0 = time.time()
while time.time() - t0 < 600 and not denoise_landed(mark):
pause(0.5)
"""Each frame at 1:1 on its point, once as Best develops it — what a
raw opens with — and once by Bilinear, the camera's ordinary
conversion, cut to the same square of the canvas. Waits for the network
rather than for a fixed time: on the CPU, which the demo profile uses,
it takes many seconds."""
for slug, photo, (fx, fy) in DENOISE_FRAMES:
mark = log_size()
at_develop(photo)
a = dr.photo(fx, fy)
dr.move(*a)
pause(0.3)
dr.x('click', '--repeat', 2, '--delay', 80, 1)
pause(1.5)
close_up(f'develop-denoise-{method.lower()}', a)
undo_all()
dr.move(*a)
dr.x('click', '--repeat', 2, '--delay', 80, 1)
pause(1.2)
t0 = time.time()
while not denoise_landed(mark):
if time.time() - t0 > 600:
raise TimeoutError(f'{photo}: Best never landed')
pause(0.5)
pause(1.5)
close_up(f'develop-denoise-{slug}-best', a)
group('Detail')
dr.click(*in_column('Bilinear@RadioButton'))
pause(3.0)
close_up(f'develop-denoise-{slug}-bilinear', a)
undo_all()
dr.move(*a)
dr.x('click', '--repeat', 2, '--delay', 80, 1)
pause(1.2)
def close_up(name, p, size=DENOISE_CLOSE_UP):