If you find that hitting the “auto” button in the GIMP levels dialog generally does the thing you’re looking for, you can batch that as described here.
Specifically, you would put this script:
(define (batch-auto-levels pattern) (let* ((filelist (cadr (file-glob pattern 1)))) (while (not (null? filelist)) (let* ((filename (car filelist)) (image (car (gimp-file-load RUN-NONINTERACTIVE filename filename))) (drawable (car (gimp-image-get-active-layer image)))) (gimp-levels-stretch drawable) (gimp-file-save RUN-NONINTERACTIVE image drawable filename filename) (gimp-image-delete image)) (set! filelist (cdr filelist)))))
into the GIMP scripts directory (~/.gimp-x.x/scripts/ or %appdata%GIMPx.xscripts on Windows) named ‘batch-auto-levels.scm’ and then run
gimp -ifd -b '(batch-auto-levels "*.jpg")' -b '(gimp-quit 0)'
within the directory containing the images. Note that this will overwrite the images – copy them to a test directory and work on that until you know the results are what you want. Also make sure that your metadata is intact (GIMP is pretty good about this these days). You can set the JPEG quality you want to use as the default in the GIMP JPEG export dialog, then quit GIMP before running the batch script.