💾 Archived View for rawtext.club › ~jmq › recycled › mvi-jpg-crop.gmi captured on 2023-04-26 at 13:48:16. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2023-01-29)
-=-=-=-=-=-=-
Sometime within the past five years, gripped by the urge to clean out some of the clutter in my house, I removed all the photographs from a shelf full of photo albums and donated the empty binders to a nearby thrift store. The photographs themselves I stuffed into a couple of shoeboxes. My long-term plan was to scan the photographs and keep only the digital copies, appropriately tagged and sorted into folders. This plan would take on various shapes in the subsequent years, as I migrated from OS X to FreeBSD and later a CRUX Linux desktop.
The photo app that came with OS X did have the nifty feature of automatically detecting the boundaries of each picture (when five or six such pictures were placed on my flatbed scanner), but manual correction was required so often that I ended up looking for a better solution. In total, I only digitized about a hundred pictures using Apple's software.
While running FreeBSD, I came up with a pretty decent workflow for digitizing pictures, thanks to the informative status bar in mtPaint. After selecting a region with the mouse, I could find in the status bar the offset of the upper-left corner and the size of the selected region. This information remained visible if I moused over a terminal emulator, where I could run a command like
jpegtran -crop 800x600+150+2189 ~bruder/Scan_7145.jpg > new-mexico-vacation-12.jpg
to save the selected region with a more informative filename. Selecting a region by hand, copying the coordinates into the terminal emulator, and coming up with a good filename, were the slowest parts of the process, but after dozens of photos in a row I could easily get into a rhythm and speed through dozens more.
Lately the luxury of multi-hour gaps in my schedule has been harder to come by, so the manual integration of mtPaint and jpegtran would proceed at the slow starting pace every time, never having the chance to achieve a state of "flow" and move more quickly. I began to look for a solution that eliminated as much friction as possible.
This past summer I made the leap to a Wayland-only desktop, installing only the bare minimum of X11 libraries and eschewing any software that required an X server. As a result, I could no longer consider mtPaint (with its informative status bar) a viable solution to the photo cropping problem. Thankfully the road to a Wayland-only desktop had been mapped out by more adventurous users, and they wrote up some helpful lists of replacements for X11-only programs. I pursued two of the recommended photo viewing apps: imv by Harry Jeffries, and mpv-image-viewer (mvi for short).
imv did expose a neat method for launching a script from within the program, but it did not export enough environment variables for accurate calculation of the correct jpegtran arguments. I did manage to get "close-enough" crops with a script launched from imv, but it required aligning the upper-left corner of the scanned image with the upper-left corner of the viewing window.
The next possibility I explored was mvi, a suite of config files and scripts that turns mpv into a viewer for still photos. Thanks to the thorough documentation of mpv's Lua scripting interface, I was able to identify the methods that could construct the necessary jpegtran command automatically, upon releasing the mouse from a "select region" operation. Two new blocks of code are enough to do the job, one inside the refresh() function:
draw_ass(a.text) if opts.save_crop then mp.set_property("file-local-options/osd-msg2", string.format("%ix%i+%i+%i", line_end.image[1] - line_start.image[1], line_end.image[2] - line_start.image[2], line_start.image[1], line_start.image[2])) end
and another inside the next_step() function:
elseif state == 2 then local dim = mp.get_property_native("osd-dimensions") if not dim then return end state = 3 second_point = cursor_video_space_normalized(dim) if opts.save_crop then cropdims = mp.get_property("file-local-options/osd-msg2") filehandle = mp.get_property("path") mp.command_native({ name="subprocess", playback_only=true, args={"jpegtran","-crop",cropdims,"-outfile","newcrop.jpg",filehandle} }) end if opts.clear_on_second_point_set then next_step() end
I inserted these two blocks into the mvi script "ruler". To enable them, I just need to ensure that ${MVI_CONFIG_DIR}/script-opts/ruler.conf has a line that reads save_crop=yes. Then I can obtain a crop of the current image with just one keystroke and two mouse clicks. A follow-up bash script called "ncrop" takes one argument and uses that argument to rename the newly-created jpeg file.
During a four-month stint of being separated from my flatbed scanner, I still found it useful to have this cropping functionality in mvi. Pictures uploaded from a smart phone, or screenshots taken inside a compositor that does not implement the layer-shell protocol, all usually include more pixels than I want to use. Being able to crop them with a keystroke and two mouse clicks is an immense productivity booster.