Portrait of Michael Limberger

Michael Limberger

Need me? Email mike@limberger.ca

AI

Building Your Own Automation

The pieces you already have

Encode an image for the API. Send a request to Ollama. Craft prompts that give structured output. Parse the response with regex. Make a pass or fail decision.

From here, a full automation pipeline is up to you. There is no part 07 in this session. The numbering jumps. That is how the notes arrived.

A folder of images

Loop through a folder and check each one:

Show me.

for img in *.png; do
    echo "Checking $img..."

    IMAGE_B64=$(base64 -i "$img" | perl -pe's~\s~~g')

    RESPONSE=$(curl -s http://localhost:11434/api/generate \
      -H "Content-Type: application/json" \
      -d '{
        "model": "ministral",
        "prompt": "Answer YES or NO only.\n\nNUDE_CHEST: YES or NO",
        "images": ["'"$IMAGE_B64"'"],
        "stream": false
      }' | jq -r '.response')

    if echo "$RESPONSE" | grep -qi "NUDE_CHEST: YES"; then
        echo "  REJECT"
    else
        echo "  PASS"
    fi
done

Move rejects, do not delete them

Put rejects in a separate folder for review:

Show me.

mkdir -p ./rejects

for img in *.png; do
    # ... run your check ...

    if echo "$RESPONSE" | grep -qi "NUDE_CHEST: YES"; then
        mv "$img" ./rejects/
        echo "Moved to rejects: $img"
    fi
done

A log you can read later

Keep a record of what was checked and why:

Show me.

LOGFILE="moderation.log"

for img in images/*.png; do
    # ... run your check ...

    TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')

    if echo "$RESPONSE" | grep -qi "NUDE_CHEST: YES"; then
        RESULT="REJECT"
        REASON="Nudity detected"
    else
        RESULT="PASS"
        REASON=""
    fi

    echo "$TIMESTAMP,$img,$RESULT,$REASON" >> "$LOGFILE"
done

That creates a CSV-style log you can review later. CSV is comma-separated values: one row per line, fields split by commas.

Different prompts, different jobs

You could run different prompts for different concerns: content moderation (what we showed), quality assessment (artifacts, extra fingers), style matching, text detection (is there readable text in the image?).

Each check is just a different prompt with different parsing logic.

Temperature and friends

You can add an options block to your JSON request. The temperature setting controls randomness:

"options": {
  "temperature": 0.1
}

0.1 is more predictable, consistent. 0.7 is more creative, varied. 1.0 is most random. For moderation, lower is better. We want consistent yes or no answers, not creative interpretation.

Other useful options: "num_ctx": 4096 (context window size, how much the model can hold in one go) and "num_predict": 100 (max tokens to generate). A token is a chunk of text the model counts, roughly a word or part of a word.

It will be slow

Vision models are slower than text-only models. A single image analysis might take 5 to 30 seconds depending on your hardware and model size.

For hundreds of images: process during off-hours, consider a smaller and faster model for initial screening, use a more thorough model for borderline cases, cache results so you do not re-check unchanged images.

The same plumbing, other jobs

Encode, prompt, parse, act. That pattern works for accessibility (alt-text), organization (auto-tag photos), quality control (blurry or corrupted images), document processing (extract text from screenshots), and security (detect sensitive information in images).

Once you understand the pattern, you can adapt it to almost any image analysis task. The prompt is what you change. The plumbing stays the same.

Build something, see what works, and iterate.