New to opencv. Been playing around with examples that seem to be overkill for my seemingly simple application of the library. I need to detect all the small magenta and blue circles in an image. They are all similar size but may be overlapping each other and/or obscured by other artifacts in the image. I need to output the pixel coordinates of the center of each circle and the text of the closest label that is the same color. Any pointers to what opencv functions to use to get this done would be great!
don’t be so abstract. most people asking here are being abstract. that hinders problem solving.
if you just needed that done for that one picture, you’d be done quicker just doing it by hand, rather than cooking up any computer vision solution.
no need to scrape anything off a picture of a map though.
check out Open Street Maps
that’s not just a (start to a) replacement of google maps. there are ways to query the underlying data and get the results.
no matter what interests you, someone probably already entered the data into OSM.
those circles appear to be airports/airstrips or associated radio installations. you can be sure someone already mapped those.
Tagging: Aeroways - OpenStreetMap Wiki
Query: overpass turbo
Screenshot:
and if you click on the data tab (top right)
you get a bunch of JSON data. excerpt:
"bounds": {
"minlat": 38.7773945,
"minlon": -93.8100446,
"maxlat": 38.7970305,
"maxlon": -93.7955714
},
"geometry": [
{ "lat": 38.7811164, "lon": -93.8053024 },
{ "lat": 38.7814077, "lon": -93.8056419 },
{ "lat": 38.7843112, "lon": -93.8090254 },
...
],
"tags": {
"addr:state": "MO",
"aeroway": "aerodrome",
"closest_town": "Warrensburg, Missouri",
"ele": "242",
"gnis:county_name": "Johnson",
"gnis:created": "08/01/1991",
"gnis:feature_id": "758981",
"gnis:feature_type": "Airport",
"icao": "KRCM",
"is_in": "US",
"loc_ref": "9K4",
"name": "Swisher Skyhaven Airport",
"name:en": "Swisher Skyhaven Airport",
"name_1": "Skyhaven Airport",
"operator": "University of Central Missouri (UCM/CMSU)",
"source": "wikipedia",
"source_ref": "geonames.usgs.gov",
"type": "public",
"wikidata": "Q7537734",
"wikipedia": "en:Skyhaven Airport (Missouri)"
}
Not sure what you mean by being abstract. I thought I posted a good representation of what I am trying to use OpenCV to search for in that sample? The actual image is 20MB.
Yes they are airports on a VFR sectional. I need to find the exact pixel coordinates of each station (airport) so I can calculate their exact location on the printed image. Google maps won’t help me. I don’t need geographic coordinates.
The actual image has about 2000 of those circles. I was in the process of doing them by hand then decided to try to use ChatGPT which failed, then decided to try OpenCV. That is where I am at.
Sounds like you are trying to telling me OpenCV isn’t a good tool for this sort of task.
“abstract” in that you didn’t just come out and say it’s airports.
I’m trying to tell you that the data exists already.
sure you can use OpenCV to scrape this data off a picture but why bother when the data already exists?
I hope the AI told you about matchTemplate()
because I didn’t see you mention any of the things you tried so far, or those that you considered so far, or the approaches at the AI told you about.
I didn’t think there was any reason to mansplane the image. Lol. I think I posted an adequate representation of the input and did a good job of describing what I wanted to use OpenCV to find on there. Also who cares if the information was already out there on the internet in some other form? I was asking for pointers using OpenCV to generate it. Isn’t that what this forum is for?
As I mentioned before I don’t need geographical coordinates. I need pixel coordinates off the input image so I can map each airfield to an exact position on the paper when it is printed on based on the image size and DPI settings both of which may change.
Finally some useful information. I will explore the matchTemplate() function. It is unfortunate the person who was assigned to moderate this group attacks newbies to OpenCV this way.
After much help from ChatGPT and much trial and error I have come up with the following code for anyone else out there looking to do something similar. Not perfect but does help the tedious process of finding the pixel coordinates of thousands of these circles. I don’t know if I went in the correct direction to solve this problem as I got little guidance from the very unhelpful moderators in this group. Shame really. Such a cool library.
import cv2
import numpy as np
import matplotlib.pyplot as plt
import pytesseract
import re
# Load the image using OpenCV
image_path = "/mnt/data/chart.png"
image_cv = cv2.imread(image_path)
image_rgb = cv2.cvtColor(image_cv, cv2.COLOR_BGR2RGB)
# Convert the image to grayscale
gray = cv2.cvtColor(image_rgb, cv2.COLOR_RGB2GRAY)
# Create a mask for blue and magenta colors in HSV color space for better accuracy
image_hsv = cv2.cvtColor(image_rgb, cv2.COLOR_RGB2HSV)
# Define the color range for blue and magenta in HSV color space
lower_blue_hsv = np.array([100, 150, 0])
upper_blue_hsv = np.array([140, 255, 255])
lower_magenta_hsv = np.array([140, 100, 100])
upper_magenta_hsv = np.array([170, 255, 255])
# Create masks for blue and magenta colors
blue_mask_hsv = cv2.inRange(image_hsv, lower_blue_hsv, upper_blue_hsv)
magenta_mask_hsv = cv2.inRange(image_hsv, lower_magenta_hsv, upper_magenta_hsv)
# Combine the masks
combined_mask_hsv = cv2.bitwise_or(blue_mask_hsv, magenta_mask_hsv)
# Find contours in the mask
contours, _ = cv2.findContours(combined_mask_hsv, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# Filter contours by size to match the small circles with a higher minimum radius
filtered_centers = []
for contour in contours:
((x, y), radius) = cv2.minEnclosingCircle(contour)
if 15 <= radius <= 20: # Adjust the radius range based on the expected circle size
filtered_centers.append((int(x), int(y)))
# Extract text labels from the image using Tesseract OCR
text_data = pytesseract.image_to_data(image_rgb, output_type=pytesseract.Output.DICT)
# Function to filter valid text labels
def filter_valid_labels(text_data):
valid_labels = []
label_pattern = re.compile(r'^[A-Z0-9]{3,4}$') # Regex to match 3-4 character labels
for i, text in enumerate(text_data['text']):
text = text.strip()
if label_pattern.match(text):
valid_labels.append({
'text': text,
'x': text_data['left'][i] + text_data['width'][i] // 2,
'y': text_data['top'][i] + text_data['height'][i] // 2,
'color': image_hsv[text_data['top'][i] + text_data['height'][i] // 2, text_data['left'][i] + text_data['width'][i] // 2]
})
return valid_labels
# Filter valid labels from the extracted text data
valid_labels = filter_valid_labels(text_data)
# Function to find the nearest valid text label for each circle center
def find_nearest_valid_label(coord, valid_labels, mask_hsv):
x, y = coord
min_dist = float('inf')
nearest_label = ""
for label in valid_labels:
if np.all(mask_hsv[label['y'], label['x']] == combined_mask_hsv[y, x]):
dist = np.sqrt((x - label['x'])**2 + (y - label['y'])**2)
if dist < min_dist:
min_dist = dist
nearest_label = label['text']
return nearest_label
# Find the nearest valid text label for each detected center
labeled_centers = [(coord, find_nearest_valid_label(coord, valid_labels, combined_mask_hsv)) for coord in filtered_centers]
# Plot the image with labeled circle centers
plt.figure(figsize=(20, 20))
plt.imshow(image_rgb)
for (x, y), label in labeled_centers:
plt.plot(x, y, 'ro')
plt.text(x, y, label, color='white', fontsize=12, ha='center', va='center', bbox=dict(facecolor='black', alpha=0.5))
plt.title("Detected Circle Centers with Valid Labels")
plt.show()
# Return the labeled centers
labeled_centers