Overview
========
This directory contains a couple of utilities to enable automated processing with MTF Mapper.
It does not really support real-time processing, but depending on the input image complexity
(number of edges, number of pixels in each edge, image resolution) this could be acceptable;
it certainly beats having to manually load each image into the GUI.

From within this directory you can build the container using the command 
make build

But you can also pull an existing docker container from docker.io/fvdbergh/mtfmapper_python ; if
you are familiar with docker building, you can use one of the existing container images as base
and simply overwrite the python scripts as needed.

Tools: process.py
=================
I have included two tools here: process.py and watcher.py. The process.py script is intended for
processing a single input image with MTF Mapper, and reading the per-edge measurements back. These
measurements include MTF50 as well as the full SFR, conveniently made available as Python data
structures. Take a look at the mean_mtf() function in process.py to get an idea of what you can
do with the raw measurements; the provided example simply averages the MTF50 values, which is
good enough for focusing a lens using MTF Mapper. Maybe for focusing you only want to look at
the MTF50 values closer to the center of the image, which you can implement quite easily by
using the per-edge image coordinates provided.

Anyway, you can run process.py with a docker command like the following one:
docker run --rm -it -v /home/fvdbergh/input:/arb_in fvdbergh/mtfmapper_python:v0.7.39 process.py --input /arb_in/sample.ppm

Note that you have to mount a host directory, /home/fvdbergh/input, to another arbitrary directory
in the docker container, /arb_in. The input image filename is specified with the --input parameter
to the process.py script: note that the input image name is specified relative to the host-mounted
directory name inside the container, here it is /arb_in/sample.ppm.

You can add additional MTF Mapper parameters by appending them with the --args parameter, e.g., 
docker run --rm -it -v /home/fvdbergh/input:/arb_in fvdbergh/mtfmapper_python:v0.7.39 \
  process.py --input /arb_in/sample.ppm --args "-t 0.4 --bayer green"

Note that process.py examines the file extension to decide whether a given image file can be
processed directly by MTF Mapper, e.g., .jpg, .png, .tif, or .ppm files. If not, it will
call the LibRaw dcraw_emu utility to convert the raw image (e.g., .nef, .cr2, .dng) into a
16-bit linear intensity image. In other words, the behaviour of process.py is similar to
that of the MTF Mapper GUI. Just take note that if you pass a non-raw image (e.g., .tif) 
image to process.py then the normal MTF Mapper assumptions apply: 8-bit images are assumed
to be encoded with the sRGB non-linear tone curve, unless the image file contains an ICC
profile. You can force process.py to treat images (8-bit and 16-bit) as if they have a linear
intensity encode by passing --args "--linear" to process.py (or append --linear to your
exising --args string).

Tools: process.py
=================
The watcher.py script is a way of repeatedly calling process.py in an automated fashion. It
does this by watching a directory for CLOSE_WRITE events, meaning that watcher.py is notified
every time a file is closed after being written to. The watcher.py script then examines the 
extension of the newly-created file, and if it decides that this is an image file it will 
pass the image on to process.py for MTF processing. All you need to obtain automated MTF
processing is to have another program periodically create image files captured by your
camera in the directory that watcher.py is watching.

To run the watcher.py script you can use the following template:
docker run --rm -it -v /home/fvdbergh/images:/watchdir fvdbergh/mtfmapper_python:v0.7.39 watcher.py --path /watchdir

Leave the docker container running watcher.py, and in another terminal you have to create 
images in the directory /home/fvdbergh/images on the host machine where the docker container
is running. For example, if you have a v4l2 compatible camera attached you can run 
something like this:
gst-launch-1.0 v4l2src ! queue ! videoconvert ! queue ! videoconvert ! pnmenc \
  ! multifilesink location=/home/fvdbergh/images/image_%06d.ppm

Now watcher.py should call process.py every time a new image_000*.ppm file is saved
by the gstreamer pipeline. Well, almost every file, since watcher.py is aware that process.py
may not be able to keep up with the rate at which the images arrive. As a result, watcher.py
will ignore any images that arrive in the watched directory while process.py is still running;
so effectively watcher.py will drop frames to keep up. For interactive adjustment of lens focus
this is probably the behaviour you want anyway to minimize lag. For best results I recommend
that you adjust your v4l2src capture frame rate to minimize the number of dropped frames.

Note that watcher.py does not delete any files on its own, so be aware that the gstreamer
pipeline example above will eventually fill up the disk. You can mitigate this by adding
changing the last stage in the gstreamer pipeline to
  ! multifilesink location=/home/fvdbergh/images/image_%06d.ppm max-files=50
where max-files is a suitably large number so that the pipeline does not overwrite any images
before watcher.py / process.py had a chance to read them.

Conclusion
==========
So that's about it for this collection of tools. The handling (aggregation) of the extracted
MTF50 values shown in the mean_mtf() function of process.py is very basic on purpose: it just
prints out the mean MTF50 value across all detected edges. It is enough to demonstrate the
basics of building an interactive lens focusing tool, though. For example, rather than just
printing out the mean MTF50 value you could append it to a text file. You could then have another
Python script monitoring the text file using inotify, for example, to read the mean MTF50
values as they are created, and plotting them dynamically as a time series. To focus a lens
you then adjust the lens until you obtain a clear peak in the MTF50 time series. Adding 
a horizontal line to this plot to indicate the highest MTF50 value observed in the session,
like a high-water mark, will help.

