A tool that sorts image batches into three folders.
In Summer 2025 I built PhotoValidator as a task for the IT team of the Faculty of Engineering at Ain Shams University. You point it at a folder of images. It sorts them into Valid, Invalid and ManualReview folders and writes logs that say why each file landed where it did.
The targets in its code are images for the faculty website (slider images at 1920x705 and gallery images at 800x600) and for publications (A4 and B5, for screen and print). It looks for wrong sizes or formats, added borders, watermarks, overlaid text and heavy editing.
The IT team and the Computer and Systems Engineering Department supervised the task. I had little computer vision background when I started; the report I wrote says so.

Five checks, three outcomes.
The diagram at the top shows the flow. Each check is its own module, so I could tune and test it on its own.
A file ends up in Valid, in Invalid (split by the check that failed) or in ManualReview. If any check marks a file invalid, that wins over manual review. A file is valid only if no check flagged it.
- Specifications
- Size, DPI, format, colour mode and file size against a table of targets. It reads file metadata only, so it is cheap.
- Border
- Plain OpenCV, no model: Canny edges, morphological closing and contour checks find added frames. Early versions flagged carpets and fabric, so I added adaptive thresholds and guards for vignetting and compression artefacts.
- Text
- PaddleOCR’s text detector finds text regions and scores each one’s confidence.
- Watermark
- A pretrained ConvNeXt classifier (convnext-tiny) from the open-source
boomb0om/watermark-detectorsproject. I integrated and thresholded it; I did not train it. - Quality
- BRISQUE, NIQE and CLIP-IQA scores from PyIQA, plus histogram, edge and frequency features, combined into a 0 to 100 editing score. It measures over-editing such as sharpening, heavy saturation and smoothing, not image quality in general. When the learned metrics and my features agree, the metrics count for more; when they disagree, the blend becomes more cautious.
Manual review is a result, not a failure.
I did not want a yes-or-no answer. Each detector has a band between “fine” and “reject” where it hands the file to a person.
- Text
- Invalid above 80% confidence or more than five regions. Manual review above 60% confidence or more than two regions.
- Watermark
- Invalid above 96%. Manual review from 91% to 96%.
- Editing score
- Invalid at 30 or more. Manual review at 25 or more.
There are two ways to run the checks. The launcher’s full routine scan runs four scripts (specifications, border, watermark, quality) one after another over the whole folder, then merges the results with the precedence above. The text check is a separate scan there.
The main controller runs every check on each image by default and keeps all the reasons. With --sequential-mode it stops at the first failed check. It spreads work across images with a worker pool, four workers by default, not across checks inside one image. My report draws the checks as parallel threads; the code does not.
What the checks look like.
My report follows one courtyard test photo through the steps that feed the border check, then shows what the border and text detectors return. These four figures come from it. Each verdict is one worked example, not a measurement.




A 55-page report, written as I built it.
The development report takes the system apart: the architecture, every image-processing step with its intermediate images, each detector, the quality model and worked examples with real outputs. Hover a page to lift it, click to read it at full size, or open the whole report below.
What I tried and dropped.
- Tesseract, then PaddleOCR. I started with Tesseract. It ran into limits on complex layouts, rotated text and subtle watermarks. After evaluating alternatives and taking my supervisor’s advice, I switched to PaddleOCR’s text detector.
- A border CNN. I spent three days training CNN models for border detection and dropped them as unneeded complexity. I also tried pyCNN: good results, but too slow. Classical computer vision turned out to be easier to interpret and cheaper to run.
- My own watermark classifiers. I generated a very large synthetic dataset (more than 470,000 images) by placing logos at different positions and opacities, and trained SVM and random forest classifiers on hand-made features. They were too complex and generalised poorly, so I switched to the pretrained ConvNeXt model.
- Automatic cropping and an overlay-graphics detector. I dropped both for complexity. Manual cropping often gave better results.
- A library conflict. PaddleOCR’s wrapper moves images into its own result folders, which clashed with my output tree. I patched that method to do nothing at runtime, so the controller owns every folder.
How well it works.
My report lists accuracy ranges: 95 to 98% for text, 90 to 95% for borders, 93 to 96% for watermarks, 85 to 92% for quality and 98 to 99% for specifications. I got them by checking outputs photo by photo. I did not keep a labelled test set, so treat them as my working estimates, not a benchmark. The watermark range matches the accuracy advertised for the pretrained model, so it is not a measurement on these images.
Speed is less settled. The report says one shared image load cut processing from about one second to 0.2 to 0.35 seconds per image, but its later sections give slower figures. When I re-ran the tool in September 2026 on a laptop with 12 test files, the quality check alone took about four seconds per image. I would re-measure before quoting any speed.
Limits.
- The certificate confirms I completed the task. My records do not show whether or where the tool was deployed, or how much it was used.
- No labelled test set, so the accuracy figures above are estimates.
- The specification check covers only the website and publication targets in its table.
- The quality check scores over-editing. It does not judge composition or whether a photo is a good one.
The report and the certificate.
In September 2026 I added a terminal interface with live counters, a results table and a queue view, plus launcher scripts, so a batch can be run without remembering command-line flags. They are not part of the 2025 task.








