-
Notifications
You must be signed in to change notification settings - Fork 2
Use cases
When packJPG is the right tool, when it isn't, and what people actually use it for.
You have a folder of family photos, a wedding shoot, a client deliverable — JPEGs you'll keep for decades but rarely access. packJPG cuts ~20 % off the storage with bit-exact recovery on demand. Cheaper backup bills, identical originals when you need them.
packJPG a -r -od /backup/photos /source/photos
Git LFS billing scales with bandwidth and storage. Storing .pjg instead of .jpg shaves 20 % off both axes for free, and the decode is fast enough that a pre-commit / post-checkout hook can transparently convert.
Flickr, DeviantArt, ArtStation, Wikimedia Commons, photo stock sites — anywhere users upload a JPEG and expect to download the same JPEG byte-for-byte. Storing in .pjg form on the server saves hosting cost; decode on demand when the user clicks "download original".
This is where re-encoding to WebP/JXL/AVIF would break the contract. The user uploaded a JPEG; they expect a JPEG back. packJPG keeps that promise.
When a JPEG bounces through messaging platforms, each platform's recompression destroys quality. Pre-compressing with packJPG and asking the recipient to decode preserves the original. Niche but real for photographers exchanging proofs.
Any context where the JPEG bytes themselves are evidence — chain-of-custody requires the bit-exact file. packJPG's roundtrip guarantees this; lossy re-encoders don't.
Mobile uploads, satellite links, slow rural internet. Compressing with packJPG before transmission and decoding on arrival saves ~20 % on every transfer.
Tier-2 / tier-3 storage (S3 Glacier, etc.) where retrieval is rare. Apply packJPG before the cold-storage upload, decode on retrieval. The CPU cost amortizes against months of cheaper storage.
Browsers don't render .pjg. Don't put .pjg files behind a <img src>. Use WebP / JXL / AVIF for HTTP delivery — they're smaller AND browser-native.
packJPG is lossless. If your goal is "make the JPEG smaller at a quality cost", use a re-encoder (mozjpeg, guetzli, cjxl, imagemagick -quality). packJPG won't do that — by design.
PNG, WebP, HEIC, RAW formats — none of these are JPEG. packJPG won't touch them. For PNG, see packPNG (sister project, same author).
JPEG-encoded video frames (MJPEG, motion-JPEG security cameras) work in principle, but the per-frame overhead is high. Real-time pipelines should use a video codec, not per-frame packJPG.
If a JPEG has been through several lossy round-trips (e.g., uploaded → resized → re-encoded → re-uploaded by various platforms), the redundancy structure is already disturbed and packJPG yields modest gains. It still works, just less impressively.
#!/bin/sh
# Compress new JPEGs in $SRC into $DST as .pjg, daily.
SRC=/photos
DST=/backup/photos.pjg
find "$SRC" -name '*.jpg' -newer "$DST/.lastrun" \
| xargs -P4 -I{} packJPG a -od"$DST" {}
touch "$DST/.lastrun"Build packJPG as a static library:
make lib
Link against packJPGlib.a, include packjpglib.h. The library API exposes encode/decode for in-memory buffers — useful for embedding in upload pipelines, image-hosting backends, etc.
# .git/hooks/pre-commit
for f in $(git diff --cached --name-only --diff-filter=A | grep '\.jpg$'); do
packJPG a -np "$f"
git rm --cached "$f"
git add "${f%.jpg}.pjg"
done(Don't actually deploy this without thinking about the team workflow — this is a sketch.)
| Input | Typical .pjg size |
|---|---|
| 5 MB DSLR JPEG, quality 95 | ~3.9 MB (-22 %) |
| 1 MB phone photo, quality 90 | ~810 KB (-19 %) |
| 50 KB thumbnail, quality 80 | ~42 KB (-16 %) |
| 100 KB scanner output, quality 100 | ~75 KB (-25 %) |
| 10 KB favicon-style JPEG | ~9 KB (overhead dominates at this size) |
CPU cost on a modern desktop CPU: encode at ~20-30 MB/s of raw JPEG input, decode at ~25-40 MB/s. Embarrassingly parallel via -thN.