| name | count-objects-in-frames-using-template-matching |
| description | Use this skill to count coins, enemies, and turtles in grayscale keyframe images using OpenCV template matching with a provided count_objects.py script. Includes robust output parsing and fallback to direct template matching if the script is unavailable. |
Counting objects in keyframes
Step 1: Check if count_objects.py exists and understand its output
ls -la /root/count_objects.py 2>/dev/null
cat /root/count_objects.py 2>/dev/null
Step 2: Run the script and capture raw output first
Before parsing, always print the raw output to understand the format:
import subprocess
result = subprocess.run(
['python3', '/root/count_objects.py', '/root/keyframes_001.png', '/root/coin.png'],
capture_output=True, text=True
)
print("STDOUT:", repr(result.stdout))
print("STDERR:", repr(result.stderr))
print("Return code:", result.returncode)
Always use python3 — the environment may not have python aliased.
Step 3: Parse the output correctly
Based on the raw output, write a parser. Common output formats:
- A single number on its own line
- "Found X matches"
- "Count: X"
Robust parsing approach — extract the count carefully:
import re
def parse_count(stdout_text):
"""Parse the count from script output. Handles various formats."""
text = stdout_text.strip()
if text.isdigit():
return int(text)
patterns = [
r'[Ff]ound\s+(\d+)',
r'[Cc]ount[:\s]+(\d+)',
r'[Mm]atches[:\s]+(\d+)',
r'(\d+)\s+match',
r'(\d+)\s+found',
]
for pattern in patterns:
m = re.search(pattern, text)
if m:
return int(m.group(1))
for line in text.split('\n'):
line = line.strip()
if line.isdigit():
return int(line)
numbers = re.findall(r'\b(\d+)\b', text)
if numbers:
if len(numbers) == 1:
return int(numbers[0])
(numbers[-])
Step 4: Full counting pipeline
import subprocess
import re
import glob
import csv
def parse_count(stdout_text):
text = stdout_text.strip()
if not text:
return 0
if text.isdigit():
return int(text)
patterns = [
r'[Ff]ound\s+(\d+)',
r'[Cc]ount[:\s]+(\d+)',
r'[Mm]atches[:\s]+(\d+)',
r'(\d+)\s+match',
]
for pattern in patterns:
m = re.search(pattern, text)
if m:
return int(m.group(1))
for line in text.split('\n'):
line = line.strip()
if line.isdigit():
return int(line)
numbers = re.findall(r'\b(\d+)\b', text)
if len(numbers) == 1:
return int(numbers[0])
return 0
def count_objects(frame_path, template_path):
result = subprocess.run(
['python3', '/root/count_objects.py', frame_path, template_path],
capture_output=True, text=True, timeout=60
)
()
()
parse_count(result.stdout)
keyframes = (glob.glob())
()
templates = {
: ,
: ,
:
}
results = []
frame_path keyframes:
()
row = {: frame_path}
obj_name, tmpl_path templates.items():
()
row[obj_name] = count_objects(frame_path, tmpl_path)
results.append(row)
()
(, , newline=) f:
writer = csv.DictWriter(f, fieldnames=[, , , ])
writer.writeheader()
writer.writerows(results)
()
Fallback: Direct template matching if count_objects.py is not available
If count_objects.py doesn't exist, use OpenCV directly:
import cv2
import numpy as np
def count_objects_direct(frame_path, template_path, threshold=0.8):
"""Count objects using OpenCV template matching."""
frame = cv2.imread(frame_path, cv2.IMREAD_GRAYSCALE)
template = cv2.imread(template_path, cv2.IMREAD_GRAYSCALE)
if frame is None or template is None:
return 0
if template.shape[0] > frame.shape[0] or template.shape[1] > frame.shape[1]:
return 0
result = cv2.matchTemplate(frame, template, cv2.TM_CCOEFF_NORMED)
locations = np.where(result >= threshold)
points = list(zip(locations[1], locations[0]))
if not points:
return 0
h, w = template.shape[:2]
filtered = []
used = set()
for i, (x1, y1) in enumerate(points):
if i in used:
continue
filtered.append((x1, y1))
for j, (x2, y2) in (points):
j used j == i:
(x1 - x2) < w * (y1 - y2) < h * :
used.add(j)
(filtered)
Important notes
- Always use
python3, not python, in subprocess calls
- Do NOT convert template images to grayscale — only keyframes should be converted
- Always inspect raw output before writing the parser to ensure correctness
- Handle zero-match cases — if output is empty or says "0", return 0
- Verify keyframe count — the CSV should have exactly as many rows as extracted keyframes