An AOI machine writes its inspection archive with a doubled extension — 1234564.tif.jpg on disk — so you rename the file to .tif, .jpg or .bmp, and the load call in your C# program throws anyway, while the same file opens fine in an image viewer. Nothing is corrupt and nothing is missing: the loader and the viewer simply choose decoders differently. This guide shows how to read the true format from the file's first bytes, how to load or batch-convert misnamed images without touching the originals, which runtime settings to rule out when the extension is already right, and what to change at the writing end so the mismatch never returns.
Why does an image refuse to load after renaming the extension?
Renaming changes only the label, never the bytes, so a .tif.jpg file renamed to .tif still contains JPEG data: the loader reads the extension, opens its TIFF decoder, meets the JPEG signature at byte 0 and throws a load error.
Two decoding strategies share every inspection bench. Viewers and editors read the leading bytes, match them against known signatures and pick the fitting decoder, so almost anything image-like opens. Many machine-vision load functions treat the extension as the format declaration instead: .tif dispatches to the TIFF reader, .jpg to the JPEG reader, and a mismatch is a hard error. Your renamed copy declares TIFF while carrying JPEG bytes, so the reader rejects the header on the first check.
The rename also moves nothing in size. A 2448 × 2048 mono 8-bit frame holds 5,013,504 bytes raw:
Renaming changes zero of those bytes; only re-saving through a decoder changes the container. That is why the original loads under its .jpg name and refuses under .tif — the name is the only thing you changed.
How do you read the real format from the file's first bytes?
Open the file as bytes and look at the first four: image formats carry a fixed signature, called a magic number, at a known offset — usually byte 0 — and that signature names the true format regardless of what the file name says.
Any hex view settles it in seconds: open the file, look at bytes 0–3, done. In code it is one binary read of four bytes, with no image library involved, which is why this check belongs at the top of any import routine that accepts files from outside your own software. Every format in the table below keeps its signature at offset 0, so the first read is the only read you need. A text editor is a last resort: byte values above 127 render as stray symbols, good enough to spot a pattern but not to quote in a bug report.
Which magic bytes identify each common image format?
Compare the first four bytes against this table: FF D8 FF means JPEG, 89 50 4E 47 means PNG, 42 4D means BMP, and 49 49 2A 00 or 4D 4D 00 2A means TIFF in little- or big-endian order respectively.
| First bytes (hex) | Text view | Format |
|---|---|---|
| FF D8 FF | ÿØÿ | JPEG |
| 89 50 4E 47 | ‰PNG | PNG |
| 42 4D | BM | BMP |
| 49 49 2A 00 | II* | TIFF, little-endian |
| 4D 4D 00 2A | MM | TIFF, big-endian |
| 47 49 46 38 | GIF8 | GIF87a or GIF89a |
Read the PNG row fully: its complete signature is 8 bytes, 89 50 4E 47 0D 0A 1A 0A, but the first four already decide. The 0x89 byte prints as ‰ in the common Windows text view, which is where the ‰PNG mnemonic comes from. In the JPEG row, FF D8 is the start-of-image marker and the third FF opens the next marker block, whose second byte varies between E0, E1 and DB.
How do you load a file once its true format is known?
Three fixes, in order of preference: hand the loader a stream or memory buffer and let it detect the format from the bytes, copy the file to a name with the true extension, or use a load call that sniffs — never edit the machine's original in place.
Content dispatch beats renaming: most imaging libraries ship two entry points, a by-name loader that trusts the extension and a stream or memory loader that detects the format from the bytes. Point the second at anything another machine exported, and the name stops mattering. Where only the by-name loader exists, copy instead of rename: 1234564.tif.jpg becomes a copy named 1234564.jpg, and the JPEG loader accepts it on the first attempt.
Files are only half the story. Frames that never had a container — raw pixel buffers handed between functions — fail at decode time in their own way; the fix for JPEG decoding of raw pixel data covers that separate path.
What if the extension is already right and the load still fails?
Check bitness next: a project built as AnyCPU with the Prefer32Bit option enabled runs as a 32-bit process, and a 64-bit-only imaging runtime then fails on every file, correct extension or not — build for x64 or clear the preference.
Work the remaining causes in the order they appear on real benches. First, bitness: export a known-good image yourself, with a matching extension, and load it. If that fails too, the file was never the problem — a 32-bit process on 64-bit Windows cannot load a 64-bit-only native imaging runtime, so set the platform to x64 or clear Prefer32Bit and rebuild.
Second, truncation. A line PC that loses power mid-write leaves a stub with a perfect name. Compare the size against expectation — about 0.49 MB for the five-megapixel JPEG above, not 3 KB — and read the last bytes against the format's end marker. Extra bytes at the head of the payload raise the same class of error in MJPEG streams; the corrupt-JPEG-data fix walks through them one by one.
Third, decoder reach. Header and extension agree, size is sane, and the load still fails: check what the decoder supports, because a 16-bit TIFF loads on one runtime and not on another. Once the file does decode, the next mismatch moves into memory, where pattern matching rejects a bit depth it was not built for.
How do you fix a whole folder of misnamed images in one pass?
One scripted pass per folder: read the first four bytes of every file, map them to a format and copy each one to its true extension before loading — a 2,000-file archive at 0.5 MB each is about 1 GB of copying, seconds on an SSD.
- List every image in the folder, including the double extensions (*.tif.jpg, *.jpg.jpg and friends).
- Read the first four bytes of each file in binary mode.
- Map the bytes with the table above; send unknown signatures to a quarantine folder instead of guessing.
- Copy each file to a new name ending in its true extension, originals untouched.
- Load from the corrected copies and log whatever still refuses — its fault is no longer the name.
Keep the originals read-only so a rerun always starts from the truth.
Which hardware should run the cleanup and the conversion?
Run the batch wherever the archive lands, which on a vision line is a fanless vision controller: 8 GB of memory re-saves frame by frame, 32 GB holds roughly 6,850 decoded five-megapixel frames in RAM, and a 128 GB SSD buffers about 27,400 raw ones.
Whether the frames arrive from line smart cameras or land on one of the shop's vision controllers, the four-byte check costs nothing; disk and memory decide how much archive one pass chews through.
- LAN ports
- 5+ LAN ports
- USB 3 ports
- 3-4 USB 3 ports
- Light outputs
- 4 built-in channels
- Memory
- 8 GB
The MV-VC3301H-128G66 carries an Intel Core i3 processor, 8 GB of memory and a 128 GB SSD, plus 5+ LAN ports and 4 built-in light channels — the frame-by-frame cleanup box for a single-camera archive.
- LAN ports
- 5+ LAN ports
- USB 3 ports
- 3-4 USB 3 ports
- PoE
- PoE camera ports
- Light outputs
- 8 built-in channels
The MV-VC5525-256G70-NN steps up to a 12th-generation Intel Core i5 with 32 GB of memory, a 256 GB SSD, PoE camera ports and 8 built-in light channels, so the cleanup pass and a multi-camera inspection share one fanless box. Both classes ship with the 3-year warranty minimum.
How do you stop the double extension from coming back?
Fix the name at the writer: make the export routine take one variable that holds the format and build the extension from that same variable, so the file name and the bytes can never disagree again.
If the AOI vendor's export accepts a format parameter, bind that one variable to both the save call and the file name. If the export is closed, keep the sniff-and-copy step in your import path permanently and treat misnamed input as a normal input class, like ambient light drift. Naming at write time is the same discipline as saving triggered images with numbered filenames — one decision, in one place, with no second source of truth. Assert at ingest that header and extension agree, and a disagreeing file fails loudly at the door instead of mid-run.
To size the machine this cleanup runs on, start at the configurator, or reach us through the contact page with a stubborn file's first four bytes and we will name its format.
Questions
Why do some TIFF files start with II and others with MM?
The two letters record byte order. II, hex 49 49, marks a little-endian TIFF written Intel-style; MM, hex 4D 4D, marks a big-endian Motorola-style file. The value 42 that follows the order mark confirms the format, and the loader uses the mark to decide how it reads every multi-byte offset and pixel value deeper in the file, so a wrong order flag means nonsense dimensions.
How do I tell that a JPEG file is complete rather than truncated?
A JPEG ends with the EOI marker FF D9. Read the last two bytes: if they are not FF D9, the write was interrupted and no decoder can recover the missing tail. Compare the size with expectation too — a five-megapixel mono frame saved as JPEG is typically about 0.49 MB, so a 3 KB file is a stub. Copy what decodes, log the rest, and pull the original back from the machine if you have it.
Does the extension still matter if my loader detects the format from the bytes?
For loading, no — a content-based loader ignores the name entirely. Keep extensions truthful anyway: archive software, backup tools, transfer filters and the next engineer all trust the label, and a JPEG mislabeled as TIFF costs exactly the hour you just spent. Detection belongs in code; correct names are free at write time.
Can a renamed JPEG become a real TIFF without losing quality?
Renaming never re-encodes; converting does. Decoding the JPEG and saving the pixels as TIFF copies them losslessly, but the quality lost when the machine compressed to JPEG stays lost — JPEG is lossy by design. If the inspection needs exact sensor pixels, change the export to write real TIFF or PNG from the start.
How much smaller is a JPEG than the raw camera frame?
Typically five to twenty times smaller for industrial scenes, with texture and quality setting doing the deciding: the five-megapixel mono example is 5,013,504 bytes raw and about 0.49 MB as JPEG, close to a 10:1 ratio. A lossless PNG or uncompressed TIFF of the same noisy sensor frame is often barely smaller than raw, which is why compressed archives trade size for compression artifacts.

