Degrading jpeg images with repeated rotation - via Bash (FFmpeg and ImageMagick)



A continuation of the decade-old topic of degrading jpeg images by repeated rotation and saving. This post briefly demonstrates the process using FFmpeg and ImageMagick in a Bash script. Previously, a Python script achieving similar results was published, which has recently been updated. There are many posts on this subject and they can all be accessed by searching for 'jpeg rotation' tag.
posts: https://oioiiooixiii.blogspot.com/search/label/jpeg%20rotation
The two basic commands are below. Both versions rotate an image 90 degrees clockwise, and each overwrite the original image. They should be run inside a loop to create progressively more degraded images.

ImageMagick: The quicker of the two, it uses the standard 'libjpg' library for saving images.
mogrify -rotate "90" -quality "74" "image.jpg"

FFmpeg: Saving is done with the 'mjpeg' encoder, creating significantly different results.
ffmpeg -i "image.jpg" -vf "transpose=1" -q:v 12 "image.jpg" -y

There are many options and ways to extend each of the basic commands. For FFmpeg, one such way is to use the 'noise' filter to help create entropy in the image while running. It also has the effect of discouraging the gradual magenta-shift caused by the mjpeg encoder.

A functional (but basic) Bash script is presented later in this blog post. It allows for the choice between ImageMagick or FFmpeg versions, as well as allowing some other parameters to be set. Directly below is another montage of images created using the script. Run-time parameters for each result are given at the end of this post.



Running the script without any arguments (except for the image file name) will invoke ImageMagick's 'mogrify' command, rotating the image 500 times, and saving at a jpeg quality of '74'. Note that when the FFmpeg version is running, the jpeg quality is crudely inverted, to use the 'q:v' value of the 'mjpeg' encoder.

The parameters for the script: [filename: string] [rotations: 1-n] [quality: 1-100] [frames: (any string)] [version: (any string for FFmpeg)] [noise: 1-100]
#!/bin/bash
# Simple Bash script to degrade a jpeg image by repeated rotations and saves,
# using either FFmpeg or ImageMagick. N.B. Starting image must be a jpeg.

# Example: rotateDegrade.sh "image.jpg" "1200" "67" "no" "FFmpeg" "21"
# Run on image.jpg, 1200 rotations, quality=67, no frames, use FFmpeg, noise=21

# source: oioiiooixiii.blogspot.com
# version: 2019.08.22_13.57.37

# All relevent code resides in this function
function rotateDegrade()
{
   local rotations="${2:-500}" # number of rotations
   local quality="${3:-74}" # Jpeg save quality (note inverse value for FFmpeg)
   local saveInterim="${4:-no}" # To save every full rotation as a new frame
   local version="${5:-IM}" # Choice of function (any other string for FFmpeg)
   local ffNoise="${6:-0}" # FFmpeg noise filter

   # Name of new file created to work on
   local workingFile="${1}_r${rotations}-q${quality}-${version}-n${ffNoise}.jpg"
   cp "$1" "$workingFile" # make a copy of the input file to work on
   # N.B. consider moving above file to volatile memory e.g. /dev/shm

   # ImageMagick and FFmpeg sub-functions
   function rotateImageMagick() {
      mogrify -rotate "90" -quality "$quality" "$workingFile"; }
   function rotateFFmpeg() {
      ffmpeg -i "$workingFile" -vf "format=rgb24,transpose=1,
         noise=alls=${ffNoise}:allf=u,format=rgb24" -q:v "$((100-quality))"\
         "$workingFile" -y -loglevel panic &>/dev/null; }

   # Main loop for repeated rotations and saves
   for (( i=0;i<"$rotations";i++ ))
   {
      # Save each full rotation as a new frame (if enabled)
      [[ "$saveInterim" != "no" ]] && [[ "$(( 10#$i%4 ))" -lt 1  ]] \
      && cp "$workingFile" "$(printf %07d $((i/4)))_$workingFile"

      # Rotate by 90 degrees and save, using whichever function chosen
      [[ "$version" == "IM" ]] \
      && rotateImageMagick \
      || rotateFFmpeg

      # Display progress
      displayRotation "$i" "$rotations"
   }
}

# Simple textual feedback of progress shown in terminal
function displayRotation() { clear;
   case "$(( 10#$1%4 ))" in
   3) printf "Total: $2 / Processing: $1 πŸ‘„  ";;
   2) printf "Total: $2 / Processing: $1 πŸ‘‡  ";;
   1) printf "Total: $2 / Processing: $1 πŸ‘†  ";;
   0) printf "Total: $2 / Processing: $1 πŸ‘…  ";;
   esac
}

# Driver function
function main { rotateDegrade "$@"; echo; }; main "$@"
download: rotateDegrade.sh

python version: https://oioiiooixiii.blogspot.com/2014/08/jpeg-destruction-via-repeated-rotate.html
original image: https://www.flickr.com/photos/flowizm/19148678846/ (CC BY-NC-SA 2.0)

parameters for top image, left to right:
original | rotations=300,quality=52,version=IM | rotations=200,quality=91,version=FFmpeg,noise=7

parameters for bottom image, left to right:
rotations=208,quality=91,version=FFmpeg,noise=7 | rotations=300,quality=52,version=FFmpeg,noise=0 | rotations=500,quality=74,version=IM | rotations=1000,quality=94,version=FFmpeg,noise=7 | rotations=300,quality=94,version=FFmpeg,noise=16