Extract insights from beneficiary photos
The problem
You've got thousands of photos from activities, sessions, events but they're just sitting in folders. Which activities show highest engagement? What activities work better for different groups? Are participation patterns changing over time? Manual review would take forever and you'd miss patterns. The photos contain valuable insight about what's working but you can't access it systematically.
The solution
Use computer vision (Google Cloud Vision API) to analyse photos at scale. The AI identifies: what activities are happening (sports, arts, group work, one-to-one), engagement indicators (group size, activity type, setting), demographics patterns, and trends over time. You can query: 'Show me photos from outdoor activities in summer' or 'Which sessions have largest group sizes?' or 'Engagement patterns in arts vs sports'. Turn photos from documentation into data.
What you get
Searchable, tagged photo library with extracted insights: 'Arts activities: average 12 participants, 80% group-based. Sports activities: average 18 participants, 60% outdoor. Engagement patterns: Summer outdoor sessions 40% larger than indoor winter equivalents.' Each photo tagged with: activity type, setting, approximate participant count, date, location. You can analyse trends, find examples for reports, understand what works.
Before you start
- CRITICAL: Explicit consent from people in photos for this specific use (consent for photos ≠ consent for AI analysis)
- Photos organised by date, activity type, or location (folder structure or metadata)
- Clear policies on anonymisation and data protection for beneficiary images
- Understanding that AI analysis extracts aggregate patterns, not individual tracking
- A Google Cloud account (free tier available)
- Basic Python skills or willingness to adapt example code
When to use this
- You've got large photo libraries from activities and want to extract patterns
- You're trying to understand which activities work best but struggling with qualitative assessment
- You need to demonstrate engagement levels for funders and reports
- You want to find specific examples (outdoor activities, group work) from thousands of photos
When not to use this
- You don't have explicit consent for AI analysis of beneficiary photos - get consent first or don't do this
- Your photos contain identifiable vulnerable people and this poses safeguarding risks
- You have fewer than 100 photos - manual review is faster
- You're trying to track individuals (this is for aggregate patterns only - individual tracking raises serious ethical issues)
- Your photos are poor quality or don't show activities clearly
Steps
- 1
Verify consent and ethics
CRITICAL FIRST STEP: Check your consent forms cover AI analysis of photos, not just taking/using photos. If consent doesn't cover this, either get additional consent or don't proceed. Consider: is this analysis proportionate? Does it pose risks (re-identification, inappropriate use)? Could aggregate data still cause harm? Ethics before efficiency.
- 2
Organise your photo library
Structure folders by: activity type, date, location. Good structure: /2024/01_January/Youth_Club_Arts/ means the analysis can extract context from filenames and paths. Add basic metadata (activity name, date, location) to help interpretation. Organisation makes insights more useful. TIP: If your existing folders are messy, free tools like 'Bulk Rename Utility' (Windows) or 'Renamer' (Mac) can help standardise folder and file names before running the analysis.
- 3
Set up Cloud Vision API
Create Google Cloud account, enable Vision API, get API credentials. Free tier gives 1,000 images/month which is enough for testing. Follow Google's quickstart guide. The API returns: labels (what's in the image), object detection (specific items), text detection (signs, notices), and image properties (colours, composition).
- 4
Run analysis on sample batch
Test on 50-100 photos first. Use the example code to send images to Vision API. Check what labels it returns: are they useful? Does it identify activities accurately? Is there inappropriate detection (facial recognition when you don't want it)? Validate before processing thousands of images.
- 5
Extract activity indicators
Vision API returns labels like: 'sports', 'arts and crafts', 'group activity', 'outdoor recreation', 'team sport', 'musical instrument'. Map these to your activity categories. Also extract: people count (group size estimates), setting (indoor/outdoor), objects (sports equipment, art supplies). This becomes your structured data.
- 6
Process full photo library
Run the analysis across all photos. This might take hours for thousands of images (API rate limits). The code saves results as you go so you can resume if interrupted. Each photo gets: detected labels, activity classification, setting, approximate participant count, date from filename or metadata.
- 7
Analyse patterns and trends
Now you've got data: which activities are most common? What's the average group size by activity type? Are outdoor activities more prevalent in summer? Which programmes show high engagement (lots of group activity, diverse activities)? Aggregate analysis reveals what's working and what patterns exist.
- 8
Create searchable photo database
Tag photos with extracted insights so you can search: 'Find photos of outdoor sports activities from summer 2024' or 'Show arts activities with 10+ participants'. This makes photo libraries actually useful for reports, funding bids, communications. Discovery not just storage.
- 9
Document and protect(optional)
Record what analysis was done, why, who has access. Implement access controls - extracted data is still personal data about beneficiaries. Regular review: is this still proportionate and necessary? Delete data when no longer needed. Data protection isn't optional for beneficiary information.
Example code
Analyse photos with Cloud Vision API
This extracts activity insights from photo libraries. Requires Google Cloud Vision API credentials.
import os
import pandas as pd
from google.cloud import vision
from pathlib import Path
import json
from datetime import datetime
# IMPORTANT: Set up authentication first
# export GOOGLE_APPLICATION_CREDENTIALS="path/to/credentials.json"
client = vision.ImageAnnotatorClient()
def analyse_image(image_path):
"""
Analyse a single image using Cloud Vision API
Returns detected labels, objects, and text
"""
with open(image_path, 'rb') as image_file:
content = image_file.read()
image = vision.Image(content=content)
# Get label detection (what's in the image)
label_response = client.label_detection(image=image)
labels = [(label.description, label.score) for label in label_response.label_annotations]
# Get object detection (specific items)
object_response = client.object_localization(image=image)
objects = [(obj.name, obj.score) for obj in object_response.localized_object_annotations]
# Detect text if any (for context like activity names on boards)
text_response = client.text_detection(image=image)
texts = [text.description for text in text_response.text_annotations[:5]] if text_response.text_annotations else []
return {
'labels': labels[:10], # Top 10 labels
'objects': objects,
'text': texts
}
def classify_activity(labels, objects):
"""
Map detected labels/objects to activity categories
Adapt these mappings to your organisation's activities
"""
label_text = ' '.join([l[0].lower() for l in labels])
object_text = ' '.join([o[0].lower() for o in objects])
combined = label_text + ' ' + object_text
# Activity type detection
activity_type = None
if any(word in combined for word in ['football', 'basketball', 'sport', 'game', 'team sport']):
activity_type = 'Sports'
elif any(word in combined for word in ['art', 'paint', 'drawing', 'craft', 'creative']):
activity_type = 'Arts & Crafts'
elif any(word in combined for word in ['music', 'musical instrument', 'singing']):
activity_type = 'Music'
elif any(word in combined for word in ['cooking', 'food', 'kitchen']):
activity_type = 'Cooking'
elif any(word in combined for word in ['computer', 'laptop', 'technology']):
activity_type = 'Digital Skills'
elif any(word in combined for word in ['reading', 'book', 'education']):
activity_type = 'Learning'
else:
activity_type = 'General Activity'
# Setting detection
setting = 'Indoor' # Default
if any(word in combined for word in ['outdoor', 'park', 'field', 'grass', 'sky']):
setting = 'Outdoor'
# Engagement indicators
group_activity = any(word in combined for word in ['group', 'team', 'crowd', 'people'])
# Estimate participant count from objects
person_objects = [o for o in objects if 'person' in o[0].lower()]
estimated_participants = len(person_objects) if person_objects else None
return {
'activity_type': activity_type,
'setting': setting,
'group_activity': group_activity,
'estimated_participants': estimated_participants
}
# Process photo library
photo_directory = 'path/to/photos' # Change this
results = []
print(f"Scanning photos in {photo_directory}...")
# Walk through all images in directory
image_extensions = {'.jpg', '.jpeg', '.png'}
image_paths = []
for root, dirs, files in os.walk(photo_directory):
for file in files:
if Path(file).suffix.lower() in image_extensions:
image_paths.append(os.path.join(root, file))
print(f"Found {len(image_paths)} images")
print(f"Cost estimate: £{len(image_paths) * 0.0015:.2f} (after free tier)")
print("\nProcessing (this may take a while)...")
for idx, image_path in enumerate(image_paths):
try:
# Extract metadata from path
rel_path = os.path.relpath(image_path, photo_directory)
path_parts = Path(rel_path).parts
# Try to extract date and activity from folder structure
# Adapt this to your folder naming convention
folder_date = None
folder_activity = None
for part in path_parts:
# Try to find date (e.g., "2024" or "01_January")
if '2023' in part or '2024' in part:
folder_date = part
# Other parts might be activity names
elif part not in ['.', '..'] and not part.endswith(('.jpg', '.jpeg', '.png')):
folder_activity = part.replace('_', ' ')
# Analyse image
analysis = analyse_image(image_path)
# Classify activity
classification = classify_activity(analysis['labels'], analysis['objects'])
# Combine results
result = {
'file_path': image_path,
'folder_date': folder_date,
'folder_activity': folder_activity,
'activity_type': classification['activity_type'],
'setting': classification['setting'],
'group_activity': classification['group_activity'],
'estimated_participants': classification['estimated_participants'],
'top_labels': ', '.join([f"{l[0]} ({l[1]:.2f})" for l in analysis['labels'][:5]]),
'detected_objects': ', '.join([o[0] for o in analysis['objects']]),
'detected_text': ', '.join(analysis['text'][:3]) if analysis['text'] else ''
}
results.append(result)
# Progress update
if (idx + 1) % 50 == 0:
print(f" Processed {idx + 1}/{len(image_paths)} images...")
# Save periodically in case of interruption
if (idx + 1) % 100 == 0:
temp_df = pd.DataFrame(results)
temp_df.to_csv('photo_analysis_temp.csv', index=False)
except Exception as e:
print(f" Error processing {image_path}: {e}")
continue
# Create final dataframe
df = pd.DataFrame(results)
df.to_csv('photo_analysis_results.csv', index=False)
print(f"\nAnalysis complete! Processed {len(df)} images")
print("\nResults Summary:")
print("="*60)
# Activity type breakdown
print("\nActivity Types:")
activity_counts = df['activity_type'].value_counts()
for activity, count in activity_counts.items():
print(f" {activity}: {count} photos ({count/len(df)*100:.1f}%)")
# Setting breakdown
print("\nSettings:")
setting_counts = df['setting'].value_counts()
for setting, count in setting_counts.items():
print(f" {setting}: {count} photos ({count/len(df)*100:.1f}%)")
# Group activity prevalence
group_pct = df['group_activity'].sum() / len(df) * 100
print(f"\nGroup Activities: {group_pct:.1f}% of photos")
# Average participants (where detected)
participants_data = df[df['estimated_participants'].notna()]
if len(participants_data) > 0:
avg_participants = participants_data['estimated_participants'].mean()
print(f"\nAverage Participants (when detected): {avg_participants:.1f}")
# Activity type by setting
print("\nActivity Types by Setting:")
activity_setting = df.groupby(['activity_type', 'setting']).size().unstack(fill_value=0)
print(activity_setting)
print("\n" + "="*60)
print("Results saved to photo_analysis_results.csv")
print("\nUse this data to:")
print(" - Search photos by activity type or setting")
print(" - Analyse participation patterns over time")
print(" - Find examples for reports and communications")
print(" - Understand which activities are most common")
print("\nREMEMBER:")
print(" - This data contains information about beneficiaries")
print(" - Implement appropriate access controls")
print(" - Delete when no longer needed")
print(" - Use only for purposes covered by consent")Tools
Resources
At a glance
- Time to implement
- days
- Setup cost
- free
- Ongoing cost
- low
- Cost trend
- stable
- Organisation size
- medium, large
- Target audience
- program-delivery, data-analyst, operations-manager
Google Cloud Vision free tier: 1,000 images/month. After that: £1.50 per 1,000 images. IMPORTANT: Google charges per 'feature' requested - the example code uses label detection, object localization, and text detection, which counts as 3 billable units per image, so 1,000 images = 3,000 units against your free tier. For 5,000 photos monthly: ~£20/month. One-time analysis of historical photos (20,000 images): ~£90. Much cheaper than manual review (weeks of staff time). CRITICAL: Photos contain personal data - ensure consent covers this use and implement strong data protection.