Initial commit
This commit is contained in:
1770
label/backend/backend.py
Normal file
1770
label/backend/backend.py
Normal file
File diff suppressed because it is too large
Load Diff
583
label/backend/extract_text.py
Normal file
583
label/backend/extract_text.py
Normal file
@@ -0,0 +1,583 @@
|
||||
import boto3
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
from PIL import Image, ImageDraw, ImageFont, ImageEnhance, ImageOps, ImageFilter
|
||||
import random
|
||||
import shutil
|
||||
|
||||
# Configuration
|
||||
BUCKET_NAME = 'custom-labels-valvulas-bloco-funcao'
|
||||
FOLDER_PREFIX = 'splitted_diagrams/' # Directory in S3 (e.g., 'images/' or '' for root)
|
||||
REGION = 'us-east-1'
|
||||
OUTPUT_FOLDER = 'text_json'
|
||||
|
||||
def detect_text():
|
||||
"""Detect text using Textract, returning bounding box for each word"""
|
||||
# Initialize clients
|
||||
s3 = boto3.client('s3', region_name=REGION)
|
||||
textract = boto3.client('textract', region_name=REGION)
|
||||
|
||||
# List all PNG files in the S3 folder
|
||||
response = s3.list_objects_v2(Bucket=BUCKET_NAME, Prefix=FOLDER_PREFIX)
|
||||
|
||||
if 'Contents' not in response:
|
||||
print(f"No files found in {BUCKET_NAME}/{FOLDER_PREFIX}")
|
||||
exit()
|
||||
|
||||
# Process each PNG file
|
||||
for obj in response['Contents']:
|
||||
key = obj['Key']
|
||||
|
||||
# Skip if not a PNG file
|
||||
if not key.lower().endswith('.png'):
|
||||
continue
|
||||
|
||||
print(f"\nProcessing: {key}")
|
||||
|
||||
# Detect text using Textract
|
||||
result = textract.detect_document_text(
|
||||
Document={
|
||||
'S3Object': {
|
||||
'Bucket': BUCKET_NAME,
|
||||
'Name': key
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
# Save result to JSON file
|
||||
filename = os.path.basename(key).replace('.png', '.json').replace('.PNG', '.json')
|
||||
output_path = os.path.join(OUTPUT_FOLDER, filename)
|
||||
|
||||
os.makedirs(OUTPUT_FOLDER, exist_ok=True)
|
||||
|
||||
with open(output_path, 'w', encoding='utf-8') as f:
|
||||
json.dump(result, f, indent=2, ensure_ascii=False)
|
||||
|
||||
print(f" Saved to: {output_path}")
|
||||
|
||||
# Print detected text (per word)
|
||||
word_count = 0
|
||||
for block in result['Blocks']:
|
||||
if block['BlockType'] == 'WORD':
|
||||
word_count += 1
|
||||
text = block['Text']
|
||||
confidence = block['Confidence']
|
||||
bbox = block['Geometry']['BoundingBox']
|
||||
print(f" Word: {text} ({confidence:.1f}%) - BBox: L={bbox['Left']:.3f}, T={bbox['Top']:.3f}, W={bbox['Width']:.3f}, H={bbox['Height']:.3f}")
|
||||
|
||||
print(f" Total words detected: {word_count}")
|
||||
|
||||
|
||||
def draw_bounding_boxes(sectors_dir, json_dir, output_dir='bounding_box_images'):
|
||||
"""
|
||||
Draw bounding boxes around detected words on original images
|
||||
|
||||
Args:
|
||||
sectors_dir: Directory containing the original PNG images
|
||||
json_dir: Directory containing the JSON text detection files
|
||||
output_dir: Directory to save images with bounding boxes
|
||||
"""
|
||||
# Create output directory
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
|
||||
# Get all PNG files
|
||||
png_files = [f for f in os.listdir(sectors_dir) if f.lower().endswith('.png')]
|
||||
|
||||
for png_file in png_files:
|
||||
# Get corresponding JSON file name
|
||||
json_file = os.path.splitext(png_file)[0] + '.json'
|
||||
|
||||
image_path = os.path.join(sectors_dir, png_file)
|
||||
json_path = os.path.join(json_dir, json_file)
|
||||
output_path = os.path.join(output_dir, png_file.replace('.png', '_bbox.png'))
|
||||
|
||||
# Check if JSON file exists
|
||||
if not os.path.exists(json_path):
|
||||
print(f"Warning: JSON not found for {png_file}, skipping...")
|
||||
continue
|
||||
|
||||
print(f"Processing: {png_file}")
|
||||
|
||||
# Load the image
|
||||
img = Image.open(image_path)
|
||||
width, height = img.size
|
||||
|
||||
# Load JSON data
|
||||
with open(json_path, 'r') as f:
|
||||
data = json.load(f)
|
||||
|
||||
# Create a drawing object
|
||||
draw = ImageDraw.Draw(img)
|
||||
|
||||
# Try to use a better font, fall back to default if not available
|
||||
try:
|
||||
font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 12)
|
||||
except:
|
||||
try:
|
||||
font = ImageFont.truetype("arial.ttf", 12)
|
||||
except:
|
||||
font = ImageFont.load_default()
|
||||
|
||||
# Draw bounding box for each WORD
|
||||
word_count = 0
|
||||
for block in data['Blocks']:
|
||||
if block['BlockType'] == 'WORD':
|
||||
word_count += 1
|
||||
bbox = block['Geometry']['BoundingBox']
|
||||
|
||||
# Convert relative coordinates to absolute pixels
|
||||
left = int(bbox['Left'] * width)
|
||||
top = int(bbox['Top'] * height)
|
||||
box_width = int(bbox['Width'] * width)
|
||||
box_height = int(bbox['Height'] * height)
|
||||
|
||||
# Calculate rectangle coordinates
|
||||
x1 = left
|
||||
y1 = top
|
||||
x2 = left + box_width
|
||||
y2 = top + box_height
|
||||
|
||||
# Draw rectangle around word
|
||||
draw.rectangle([x1, y1, x2, y2], outline='red', width=2)
|
||||
|
||||
# Draw text label above bounding box
|
||||
text = block['Text']
|
||||
confidence = block['Confidence']
|
||||
label = f"{text} ({confidence:.0f}%)"
|
||||
|
||||
# Draw text background for better visibility
|
||||
try:
|
||||
text_bbox = draw.textbbox((x1, y1 - 15), label, font=font)
|
||||
draw.rectangle(text_bbox, fill='red')
|
||||
draw.text((x1, y1 - 15), label, fill='white', font=font)
|
||||
except:
|
||||
# Fallback for older Pillow versions
|
||||
draw.text((x1, y1 - 15), label, fill='red', font=font)
|
||||
|
||||
# Save the image with bounding boxes
|
||||
img.save(output_path)
|
||||
print(f" Saved: {output_path} ({word_count} bounding boxes drawn)")
|
||||
|
||||
print(f"\nAll images with bounding boxes saved to: {output_dir}")
|
||||
|
||||
|
||||
def remove_text_from_images(sectors_dir, json_dir, output_dir='cleaned_images', shrink_percent=0, keep_regex_list=None, min_confidence=0):
|
||||
"""
|
||||
Replace text bounding boxes with white pixels for all images in directory
|
||||
|
||||
Args:
|
||||
sectors_dir: Directory containing the original PNG images
|
||||
json_dir: Directory containing the JSON text detection files
|
||||
output_dir: Directory to save cleaned images
|
||||
shrink_percent: Percentage to shrink the bounding box (0-100). E.g., 10 = shrink by 10%
|
||||
keep_regex_list: List of regex patterns. Words matching these patterns will NOT be removed.
|
||||
Add "+" to the list to keep the "+" symbol.
|
||||
min_confidence: Minimum confidence threshold (0-100). Words with confidence below this will NOT be removed.
|
||||
"""
|
||||
# Create output directory
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
|
||||
# Compile regex patterns for efficiency
|
||||
compiled_patterns = []
|
||||
if keep_regex_list:
|
||||
for pattern in keep_regex_list:
|
||||
try:
|
||||
compiled_patterns.append(re.compile(pattern))
|
||||
except re.error as e:
|
||||
print(f"Warning: Invalid regex pattern '{pattern}': {e}")
|
||||
|
||||
# Get all PNG files
|
||||
png_files = [f for f in os.listdir(sectors_dir) if f.lower().endswith('.png')]
|
||||
|
||||
for png_file in png_files:
|
||||
# Get corresponding JSON file name
|
||||
json_file = os.path.splitext(png_file)[0] + '.json'
|
||||
|
||||
image_path = os.path.join(sectors_dir, png_file)
|
||||
json_path = os.path.join(json_dir, json_file)
|
||||
output_path = os.path.join(output_dir, png_file)
|
||||
|
||||
# Check if JSON file exists
|
||||
if not os.path.exists(json_path):
|
||||
print(f"Warning: JSON not found for {png_file}, skipping...")
|
||||
continue
|
||||
|
||||
print(f"Processing: {png_file}")
|
||||
|
||||
# Load the image
|
||||
img = Image.open(image_path)
|
||||
width, height = img.size
|
||||
|
||||
# Load JSON data
|
||||
with open(json_path, 'r') as f:
|
||||
data = json.load(f)
|
||||
|
||||
# Create a drawing object
|
||||
draw = ImageDraw.Draw(img)
|
||||
|
||||
# Process each text detection - NOW PER WORD
|
||||
word_count = 0
|
||||
kept_by_regex = 0
|
||||
kept_by_confidence = 0
|
||||
for block in data['Blocks']:
|
||||
if block['BlockType'] == 'WORD':
|
||||
text = block['Text']
|
||||
confidence = block['Confidence']
|
||||
|
||||
# Check if confidence is below minimum threshold
|
||||
if confidence < min_confidence:
|
||||
kept_by_confidence += 1
|
||||
print(f" Keeping word: {text} (confidence {confidence:.1f}% < {min_confidence}%)")
|
||||
continue
|
||||
|
||||
# Check if word matches any keep pattern
|
||||
should_keep = False
|
||||
if compiled_patterns:
|
||||
for pattern in compiled_patterns:
|
||||
if pattern.match(text):
|
||||
should_keep = True
|
||||
kept_by_regex += 1
|
||||
print(f" Keeping word: {text} (matches pattern)")
|
||||
break
|
||||
|
||||
# Skip removal if word should be kept
|
||||
if should_keep:
|
||||
continue
|
||||
|
||||
word_count += 1
|
||||
bbox = block['Geometry']['BoundingBox']
|
||||
|
||||
# Convert relative coordinates to absolute pixels
|
||||
left = int(bbox['Left'] * width)
|
||||
top = int(bbox['Top'] * height)
|
||||
box_width = int(bbox['Width'] * width)
|
||||
box_height = int(bbox['Height'] * height)
|
||||
|
||||
# Apply shrink percentage
|
||||
if shrink_percent > 0:
|
||||
shrink_factor = shrink_percent / 100
|
||||
width_reduction = int(box_width * shrink_factor / 2)
|
||||
height_reduction = int(box_height * shrink_factor / 2)
|
||||
|
||||
left += width_reduction
|
||||
top += height_reduction
|
||||
box_width -= width_reduction * 2
|
||||
box_height -= height_reduction * 2
|
||||
|
||||
# Draw white rectangle over the text
|
||||
draw.rectangle(
|
||||
[(left, top), (left + box_width, top + box_height)],
|
||||
fill='white'
|
||||
)
|
||||
|
||||
# Save the modified image
|
||||
img.save(output_path)
|
||||
total_kept = kept_by_regex + kept_by_confidence
|
||||
print(f" Saved: {output_path} ({word_count} words removed, {total_kept} words kept: {kept_by_regex} by regex, {kept_by_confidence} by confidence)")
|
||||
|
||||
print(f"\nAll cleaned images saved to: {output_dir}")
|
||||
def pick_random_images(source_dir, output_dir, n, seed=None):
|
||||
"""
|
||||
Pick N random images from source directory and copy them to output directory
|
||||
|
||||
Args:
|
||||
source_dir: Directory containing the cleaned images
|
||||
output_dir: Directory to save random sample of images
|
||||
n: Number of random images to pick
|
||||
seed: Random seed for reproducibility (optional)
|
||||
|
||||
Returns:
|
||||
List of selected image filenames
|
||||
"""
|
||||
# Create output directory
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
|
||||
# Get all PNG files from source directory
|
||||
png_files = [f for f in os.listdir(source_dir) if f.lower().endswith('.png')]
|
||||
|
||||
if len(png_files) == 0:
|
||||
print(f"No PNG files found in {source_dir}")
|
||||
return []
|
||||
|
||||
# Check if n is larger than available files
|
||||
if n > len(png_files):
|
||||
print(f"Warning: Requested {n} images but only {len(png_files)} available. Using all images.")
|
||||
n = len(png_files)
|
||||
|
||||
# Set random seed if provided
|
||||
if seed is not None:
|
||||
random.seed(seed)
|
||||
|
||||
# Randomly select n images
|
||||
selected_files = random.sample(png_files, n)
|
||||
|
||||
print(f"\nPicking {n} random images from {source_dir}:")
|
||||
|
||||
# Copy selected images to output directory
|
||||
for filename in selected_files:
|
||||
source_path = os.path.join(source_dir, filename)
|
||||
dest_path = os.path.join(output_dir, filename)
|
||||
|
||||
shutil.copy2(source_path, dest_path)
|
||||
print(f" Copied: {filename}")
|
||||
|
||||
print(f"\n{len(selected_files)} random images saved to: {output_dir}")
|
||||
|
||||
return selected_files
|
||||
def augment_images(source_dir, output_dir='augmented_images', augmentations_per_image=5,
|
||||
brightness_range=(0.7, 1.3), contrast_range=(0.7, 1.3),
|
||||
rotation_range=(-15, 15), blur_probability=0.3, noise_probability=0.3,
|
||||
flip_horizontal=True, flip_vertical=False, seed=None):
|
||||
"""
|
||||
Apply data augmentation to images in source directory
|
||||
|
||||
Args:
|
||||
source_dir: Directory containing the original images
|
||||
output_dir: Directory to save augmented images
|
||||
augmentations_per_image: Number of augmented versions to create per image
|
||||
brightness_range: Tuple (min, max) for brightness adjustment (1.0 = original)
|
||||
contrast_range: Tuple (min, max) for contrast adjustment (1.0 = original)
|
||||
rotation_range: Tuple (min_degrees, max_degrees) for rotation
|
||||
blur_probability: Probability of applying blur (0.0 to 1.0)
|
||||
noise_probability: Probability of adding noise (0.0 to 1.0)
|
||||
flip_horizontal: Whether to include horizontal flips
|
||||
flip_vertical: Whether to include vertical flips
|
||||
seed: Random seed for reproducibility (optional)
|
||||
|
||||
Returns:
|
||||
Total number of augmented images created
|
||||
"""
|
||||
# Create output directory
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
|
||||
# Set random seed if provided
|
||||
if seed is not None:
|
||||
random.seed(seed)
|
||||
|
||||
# Get all PNG files from source directory
|
||||
png_files = [f for f in os.listdir(source_dir) if f.lower().endswith('.png')]
|
||||
|
||||
if len(png_files) == 0:
|
||||
print(f"No PNG files found in {source_dir}")
|
||||
return 0
|
||||
|
||||
print(f"\nAugmenting {len(png_files)} images from {source_dir}:")
|
||||
print(f"Creating {augmentations_per_image} augmented versions per image")
|
||||
|
||||
total_created = 0
|
||||
|
||||
for png_file in png_files:
|
||||
image_path = os.path.join(source_dir, png_file)
|
||||
base_name = os.path.splitext(png_file)[0]
|
||||
|
||||
# Load the image
|
||||
img = Image.open(image_path)
|
||||
|
||||
print(f"\nProcessing: {png_file}")
|
||||
|
||||
for aug_idx in range(augmentations_per_image):
|
||||
# Start with a copy of the original image
|
||||
aug_img = img.copy()
|
||||
|
||||
augmentation_list = []
|
||||
|
||||
# Random brightness adjustment
|
||||
if random.random() > 0.5:
|
||||
brightness_factor = random.uniform(*brightness_range)
|
||||
enhancer = ImageEnhance.Brightness(aug_img)
|
||||
aug_img = enhancer.enhance(brightness_factor)
|
||||
augmentation_list.append(f"brightness_{brightness_factor:.2f}")
|
||||
|
||||
# Random contrast adjustment
|
||||
if random.random() > 0.5:
|
||||
contrast_factor = random.uniform(*contrast_range)
|
||||
enhancer = ImageEnhance.Contrast(aug_img)
|
||||
aug_img = enhancer.enhance(contrast_factor)
|
||||
augmentation_list.append(f"contrast_{contrast_factor:.2f}")
|
||||
|
||||
# Random rotation
|
||||
if random.random() > 0.5:
|
||||
rotation_angle = random.uniform(*rotation_range)
|
||||
aug_img = aug_img.rotate(rotation_angle, fillcolor='white', expand=False)
|
||||
augmentation_list.append(f"rotate_{rotation_angle:.1f}")
|
||||
|
||||
# Random blur
|
||||
if random.random() < blur_probability:
|
||||
blur_radius = random.uniform(0.5, 2.0)
|
||||
aug_img = aug_img.filter(ImageFilter.GaussianBlur(radius=blur_radius))
|
||||
augmentation_list.append(f"blur_{blur_radius:.1f}")
|
||||
|
||||
# Random noise (salt and pepper)
|
||||
if random.random() < noise_probability:
|
||||
aug_img = add_noise(aug_img, noise_level=0.02)
|
||||
augmentation_list.append("noise")
|
||||
|
||||
# Random horizontal flip
|
||||
if flip_horizontal and random.random() > 0.5:
|
||||
aug_img = ImageOps.mirror(aug_img)
|
||||
augmentation_list.append("flip_h")
|
||||
|
||||
# Random vertical flip
|
||||
if flip_vertical and random.random() > 0.5:
|
||||
aug_img = ImageOps.flip(aug_img)
|
||||
augmentation_list.append("flip_v")
|
||||
|
||||
# Save augmented image
|
||||
aug_suffix = "_".join(augmentation_list) if augmentation_list else "original"
|
||||
output_filename = f"{base_name}_aug{aug_idx}_{aug_suffix}.png"
|
||||
output_path = os.path.join(output_dir, output_filename)
|
||||
|
||||
aug_img.save(output_path)
|
||||
total_created += 1
|
||||
|
||||
print(f" Created: {output_filename}")
|
||||
|
||||
print(f"\n{total_created} augmented images saved to: {output_dir}")
|
||||
return total_created
|
||||
|
||||
|
||||
def add_noise(image, noise_level=0.02):
|
||||
"""
|
||||
Add salt and pepper noise to an image
|
||||
|
||||
Args:
|
||||
image: PIL Image object
|
||||
noise_level: Probability of a pixel being noisy (0.0 to 1.0)
|
||||
|
||||
Returns:
|
||||
PIL Image with noise added
|
||||
"""
|
||||
img_array = list(image.getdata())
|
||||
width, height = image.size
|
||||
|
||||
for i in range(len(img_array)):
|
||||
if random.random() < noise_level:
|
||||
# Randomly choose salt (white) or pepper (black)
|
||||
if random.random() > 0.5:
|
||||
img_array[i] = (255, 255, 255) if image.mode == 'RGB' else 255
|
||||
else:
|
||||
img_array[i] = (0, 0, 0) if image.mode == 'RGB' else 0
|
||||
|
||||
noisy_image = Image.new(image.mode, (width, height))
|
||||
noisy_image.putdata(img_array)
|
||||
|
||||
return noisy_image
|
||||
|
||||
def filter_images_by_pattern(image_dir, json_dir, output_dir_match, output_dir_no_match, pattern=r'VM-\d{4}'):
|
||||
"""
|
||||
Filter images that contain at least one word matching the specified pattern
|
||||
Creates two folders: one with matches and one without matches
|
||||
|
||||
Args:
|
||||
image_dir: Directory containing the images
|
||||
json_dir: Directory containing the JSON text detection files
|
||||
output_dir_match: Directory to save images that MATCH the pattern
|
||||
output_dir_no_match: Directory to save images that DO NOT match the pattern
|
||||
pattern: Regex pattern to match (default: VM-#### where #### is 4 digits)
|
||||
|
||||
Returns:
|
||||
Tuple of (matched_count, no_match_count, matched_files, no_match_files)
|
||||
"""
|
||||
# Create output directories
|
||||
os.makedirs(output_dir_match, exist_ok=True)
|
||||
os.makedirs(output_dir_no_match, exist_ok=True)
|
||||
|
||||
# Compile the regex pattern
|
||||
try:
|
||||
compiled_pattern = re.compile(pattern)
|
||||
except re.error as e:
|
||||
print(f"Error: Invalid regex pattern '{pattern}': {e}")
|
||||
return 0, 0, [], []
|
||||
|
||||
# Get all PNG files from image directory
|
||||
png_files = [f for f in os.listdir(image_dir) if f.lower().endswith('.png')]
|
||||
|
||||
if len(png_files) == 0:
|
||||
print(f"No PNG files found in {image_dir}")
|
||||
return 0, 0, [], []
|
||||
|
||||
print(f"\nFiltering images by pattern: {pattern}")
|
||||
print(f"Checking {len(png_files)} images...")
|
||||
|
||||
matched_count = 0
|
||||
no_match_count = 0
|
||||
matched_files = []
|
||||
no_match_files = []
|
||||
|
||||
for png_file in png_files:
|
||||
# Get corresponding JSON file name
|
||||
json_file = os.path.splitext(png_file)[0] + '.json'
|
||||
|
||||
image_path = os.path.join(image_dir, png_file)
|
||||
json_path = os.path.join(json_dir, json_file)
|
||||
|
||||
# Check if JSON file exists
|
||||
if not os.path.exists(json_path):
|
||||
print(f"Warning: JSON not found for {png_file}, skipping...")
|
||||
continue
|
||||
|
||||
# Load JSON data
|
||||
with open(json_path, 'r') as f:
|
||||
data = json.load(f)
|
||||
|
||||
# Check if any word matches the pattern
|
||||
matching_words = []
|
||||
for block in data['Blocks']:
|
||||
if block['BlockType'] == 'WORD':
|
||||
text = block['Text']
|
||||
if compiled_pattern.search(text):
|
||||
matching_words.append(text)
|
||||
|
||||
# Copy image to appropriate folder
|
||||
if matching_words:
|
||||
# Image has matching words
|
||||
matched_count += 1
|
||||
output_path = os.path.join(output_dir_match, png_file)
|
||||
shutil.copy2(image_path, output_path)
|
||||
|
||||
matched_files.append((png_file, matching_words))
|
||||
print(f" ✓ MATCH: {png_file} - Found: {', '.join(matching_words)}")
|
||||
else:
|
||||
# Image has no matching words
|
||||
no_match_count += 1
|
||||
output_path = os.path.join(output_dir_no_match, png_file)
|
||||
shutil.copy2(image_path, output_path)
|
||||
|
||||
no_match_files.append(png_file)
|
||||
print(f" ✗ NO MATCH: {png_file}")
|
||||
|
||||
print(f"\n=== Filtering Summary ===")
|
||||
print(f"Pattern: '{pattern}'")
|
||||
print(f"Total images processed: {len(png_files)}")
|
||||
print(f"Images WITH pattern: {matched_count} (saved to {output_dir_match})")
|
||||
print(f"Images WITHOUT pattern: {no_match_count} (saved to {output_dir_no_match})")
|
||||
|
||||
return matched_count, no_match_count, matched_files, no_match_files
|
||||
#matched_count, no_match_count, matched_files, no_match_files = filter_images_by_pattern(
|
||||
# './clean_image', './text_json', './vm_images', './no_vm_images'
|
||||
#)
|
||||
|
||||
# Print detailed summary
|
||||
#print("\n=== Images WITH VM-#### Pattern ===")
|
||||
#for filename, words in matched_files:
|
||||
# print(f"{filename}: {', '.join(words)}")
|
||||
|
||||
#print(f"\n=== Images WITHOUT VM-#### Pattern ===")
|
||||
#for filename in no_match_files:
|
||||
# print(f"{filename}")
|
||||
# Run text detection
|
||||
#detect_text()
|
||||
|
||||
# Draw bounding boxes on original images
|
||||
#draw_bounding_boxes('./sectors', './text_json', './bounding_box_images')
|
||||
|
||||
# Remove text from images, but keep words matching the regex patterns
|
||||
# Example: Keep "+" symbol and any words starting with "PT" or "FT"
|
||||
#remove_text_from_images('./sectors', './text_json', './clean_image', 0, [r'\+',r'.*[Xx].*',r'\1',r'L'],25)
|
||||
augment_images('./to_augment', './test_dataset',
|
||||
augmentations_per_image=1,
|
||||
rotation_range=(-5,5),
|
||||
blur_probability=0.5,
|
||||
noise_probability=0.5)
|
||||
#pick_random_images("./clean_image","./dataset",200)
|
||||
1
label/backend/notas.txt
Normal file
1
label/backend/notas.txt
Normal file
@@ -0,0 +1 @@
|
||||
* /
|
||||
81
label/backend/rekognition.py
Normal file
81
label/backend/rekognition.py
Normal file
@@ -0,0 +1,81 @@
|
||||
#Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
#PDX-License-Identifier: MIT-0 (For details, see https://github.com/awsdocs/amazon-rekognition-custom-labels-developer-guide/blob/master/LICENSE-SAMPLECODE.)
|
||||
|
||||
import boto3
|
||||
import io
|
||||
from PIL import Image, ImageDraw, ExifTags, ImageColor, ImageFont
|
||||
|
||||
def display_image(bucket,photo,response):
|
||||
# Load image from S3 bucket
|
||||
s3_connection = boto3.resource('s3')
|
||||
|
||||
s3_object = s3_connection.Object(bucket,photo)
|
||||
s3_response = s3_object.get()
|
||||
|
||||
stream = io.BytesIO(s3_response['Body'].read())
|
||||
image=Image.open(stream)
|
||||
|
||||
# Ready image to draw bounding boxes on it.
|
||||
imgWidth, imgHeight = image.size
|
||||
draw = ImageDraw.Draw(image)
|
||||
|
||||
# calculate and display bounding boxes for each detected custom label
|
||||
print('Detected custom labels for ' + photo)
|
||||
for customLabel in response['CustomLabels']:
|
||||
print('Label ' + str(customLabel['Name']))
|
||||
print('Confidence ' + str(customLabel['Confidence']))
|
||||
if 'Geometry' in customLabel:
|
||||
box = customLabel['Geometry']['BoundingBox']
|
||||
left = imgWidth * box['Left']
|
||||
top = imgHeight * box['Top']
|
||||
width = imgWidth * box['Width']
|
||||
height = imgHeight * box['Height']
|
||||
|
||||
fnt = ImageFont.truetype('/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf', 50)
|
||||
draw.text((left,top), customLabel['Name'], fill='#00d400', font=fnt)
|
||||
|
||||
print('Left: ' + '{0:.0f}'.format(left))
|
||||
print('Top: ' + '{0:.0f}'.format(top))
|
||||
print('Label Width: ' + "{0:.0f}".format(width))
|
||||
print('Label Height: ' + "{0:.0f}".format(height))
|
||||
|
||||
points = (
|
||||
(left,top),
|
||||
(left + width, top),
|
||||
(left + width, top + height),
|
||||
(left , top + height),
|
||||
(left, top))
|
||||
draw.line(points, fill='#00d400', width=5)
|
||||
|
||||
output_filename = 'output_' + photo.split('/')[-1]
|
||||
image.save(output_filename)
|
||||
print(f'\nImage saved as: {output_filename}')
|
||||
|
||||
def show_custom_labels(model,bucket,photo, min_confidence):
|
||||
client=boto3.client('rekognition')
|
||||
|
||||
#Call DetectCustomLabels
|
||||
response = client.detect_custom_labels(Image={'S3Object': {'Bucket': bucket, 'Name': photo}},
|
||||
MinConfidence=min_confidence,
|
||||
ProjectVersionArn=model)
|
||||
|
||||
# For object detection use case, uncomment below code to display image.
|
||||
|
||||
display_image(bucket,photo,response)
|
||||
|
||||
return len(response['CustomLabels'])
|
||||
|
||||
def main():
|
||||
|
||||
bucket='custom-labels-valvulas-bloco-funcao'
|
||||
photo='clean_image/DE-5400.00-4710-944-TYS-009=C_row2_col4.png'
|
||||
model='arn:aws:rekognition:us-east-1:173378533286:project/labels-valvula/version/labels-valvula.2025-11-24T15.44.16/1764009856090'
|
||||
min_confidence=80
|
||||
|
||||
|
||||
label_count=show_custom_labels(model,bucket,photo, min_confidence)
|
||||
print("Custom labels detected: " + str(label_count))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
211
label/backend/topng.py
Normal file
211
label/backend/topng.py
Normal file
@@ -0,0 +1,211 @@
|
||||
import os
|
||||
from pathlib import Path
|
||||
from pdf2image import convert_from_path
|
||||
from PIL import Image
|
||||
import json
|
||||
def convert_pdfs_to_png(input_dir, output_dir=None, dpi=300):
|
||||
"""
|
||||
Convert all PDFs in a directory to PNG images.
|
||||
|
||||
Args:
|
||||
input_dir: Directory containing PDF files
|
||||
output_dir: Directory to save PNG files (defaults to input_dir/png_output)
|
||||
dpi: Resolution for conversion (default 300 for high quality)
|
||||
"""
|
||||
input_path = Path(input_dir)
|
||||
|
||||
if not input_path.exists():
|
||||
print(f"Error: Directory '{input_dir}' does not exist")
|
||||
return
|
||||
|
||||
# Set output directory
|
||||
if output_dir is None:
|
||||
output_path = input_path / "png_output"
|
||||
else:
|
||||
output_path = Path(output_dir)
|
||||
|
||||
output_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Find all PDF files
|
||||
pdf_files = list(input_path.glob("*.pdf"))
|
||||
|
||||
if not pdf_files:
|
||||
print(f"No PDF files found in '{input_dir}'")
|
||||
return
|
||||
|
||||
print(f"Found {len(pdf_files)} PDF file(s)")
|
||||
print(f"Converting with {dpi} DPI for high quality...")
|
||||
|
||||
for pdf_file in pdf_files:
|
||||
try:
|
||||
print(f"\nProcessing: {pdf_file.name}")
|
||||
|
||||
# Convert PDF to images
|
||||
images = convert_from_path(
|
||||
pdf_file,
|
||||
dpi=dpi,
|
||||
fmt='png',
|
||||
thread_count=4 # Use multiple threads for faster conversion
|
||||
)
|
||||
|
||||
# Save each page
|
||||
for i, image in enumerate(images, start=1):
|
||||
if len(images) > 1:
|
||||
output_filename = f"{pdf_file.stem}_page_{i}.png"
|
||||
else:
|
||||
output_filename = f"{pdf_file.stem}.png"
|
||||
|
||||
output_file = output_path / output_filename
|
||||
|
||||
# Save with optimized compression
|
||||
image.save(
|
||||
output_file,
|
||||
'PNG',
|
||||
optimize=True, # Enable optimization
|
||||
compress_level=6 # Balanced compression (0-9, 6 is good balance)
|
||||
)
|
||||
|
||||
print(f" Saved: {output_filename} ({image.size[0]}x{image.size[1]}px)")
|
||||
|
||||
print(f"✓ Completed: {pdf_file.name} ({len(images)} page(s))")
|
||||
|
||||
except Exception as e:
|
||||
print(f"✗ Error processing {pdf_file.name}: {str(e)}")
|
||||
|
||||
print(f"\n{'='*50}")
|
||||
print(f"Conversion complete!")
|
||||
print(f"Output directory: {output_path.absolute()}")
|
||||
|
||||
def split_images_into_sectors(input_dir, output_dir=None, overlap_percent=1):
|
||||
"""
|
||||
Split PNG images in a directory into 25 sectors (5x5 grid) with overlap.
|
||||
|
||||
Args:
|
||||
input_dir: Directory containing PNG files
|
||||
output_dir: Directory to save split images (defaults to input_dir/sectors)
|
||||
overlap_percent: Percentage of overlap (default 1%)
|
||||
"""
|
||||
input_path = Path(input_dir)
|
||||
|
||||
if not input_path.exists():
|
||||
print(f"Error: Directory '{input_dir}' does not exist")
|
||||
return
|
||||
|
||||
# Set output directory
|
||||
if output_dir is None:
|
||||
output_path = input_path / "sectors"
|
||||
else:
|
||||
output_path = Path(output_dir)
|
||||
|
||||
output_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Find all PNG files
|
||||
png_files = list(input_path.glob("*.png"))
|
||||
|
||||
if not png_files:
|
||||
print(f"No PNG files found in '{input_dir}'")
|
||||
return
|
||||
|
||||
print(f"Found {len(png_files)} PNG file(s)")
|
||||
print(f"Splitting into 25 sectors (5x5 grid) with {overlap_percent}% overlap...")
|
||||
|
||||
for png_file in png_files:
|
||||
try:
|
||||
print(f"\nProcessing: {png_file.name}")
|
||||
|
||||
# Open image
|
||||
img = Image.open(png_file)
|
||||
width, height = img.size
|
||||
|
||||
# Calculate overlap in pixels
|
||||
h_overlap = int(width * overlap_percent / 100)
|
||||
v_overlap = int(height * overlap_percent / 100)
|
||||
|
||||
# Calculate split points for 5x5 grid
|
||||
h_split1 = width // 5
|
||||
h_split2 = 2 * width // 5
|
||||
h_split3 = 3 * width // 5
|
||||
h_split4 = 4 * width // 5
|
||||
v_split1 = height // 5
|
||||
v_split2 = 2 * height // 5
|
||||
v_split3 = 3 * height // 5
|
||||
v_split4 = 4 * height // 5
|
||||
|
||||
# Define 25 sectors with overlap (5x5 grid)
|
||||
# Format: (left, top, right, bottom)
|
||||
sectors = {
|
||||
# Row 1
|
||||
'row1_col1': (0, 0, h_split1 + h_overlap, v_split1 + v_overlap),
|
||||
'row1_col2': (h_split1 - h_overlap, 0, h_split2 + h_overlap, v_split1 + v_overlap),
|
||||
'row1_col3': (h_split2 - h_overlap, 0, h_split3 + h_overlap, v_split1 + v_overlap),
|
||||
'row1_col4': (h_split3 - h_overlap, 0, h_split4 + h_overlap, v_split1 + v_overlap),
|
||||
'row1_col5': (h_split4 - h_overlap, 0, width, v_split1 + v_overlap),
|
||||
|
||||
# Row 2
|
||||
'row2_col1': (0, v_split1 - v_overlap, h_split1 + h_overlap, v_split2 + v_overlap),
|
||||
'row2_col2': (h_split1 - h_overlap, v_split1 - v_overlap, h_split2 + h_overlap, v_split2 + v_overlap),
|
||||
'row2_col3': (h_split2 - h_overlap, v_split1 - v_overlap, h_split3 + h_overlap, v_split2 + v_overlap),
|
||||
'row2_col4': (h_split3 - h_overlap, v_split1 - v_overlap, h_split4 + h_overlap, v_split2 + v_overlap),
|
||||
'row2_col5': (h_split4 - h_overlap, v_split1 - v_overlap, width, v_split2 + v_overlap),
|
||||
|
||||
# Row 3
|
||||
'row3_col1': (0, v_split2 - v_overlap, h_split1 + h_overlap, v_split3 + v_overlap),
|
||||
'row3_col2': (h_split1 - h_overlap, v_split2 - v_overlap, h_split2 + h_overlap, v_split3 + v_overlap),
|
||||
'row3_col3': (h_split2 - h_overlap, v_split2 - v_overlap, h_split3 + h_overlap, v_split3 + v_overlap),
|
||||
'row3_col4': (h_split3 - h_overlap, v_split2 - v_overlap, h_split4 + h_overlap, v_split3 + v_overlap),
|
||||
'row3_col5': (h_split4 - h_overlap, v_split2 - v_overlap, width, v_split3 + v_overlap),
|
||||
|
||||
# Row 4
|
||||
'row4_col1': (0, v_split3 - v_overlap, h_split1 + h_overlap, v_split4 + v_overlap),
|
||||
'row4_col2': (h_split1 - h_overlap, v_split3 - v_overlap, h_split2 + h_overlap, v_split4 + v_overlap),
|
||||
'row4_col3': (h_split2 - h_overlap, v_split3 - v_overlap, h_split3 + h_overlap, v_split4 + v_overlap),
|
||||
'row4_col4': (h_split3 - h_overlap, v_split3 - v_overlap, h_split4 + h_overlap, v_split4 + v_overlap),
|
||||
'row4_col5': (h_split4 - h_overlap, v_split3 - v_overlap, width, v_split4 + v_overlap),
|
||||
|
||||
# Row 5
|
||||
'row5_col1': (0, v_split4 - v_overlap, h_split1 + h_overlap, height),
|
||||
'row5_col2': (h_split1 - h_overlap, v_split4 - v_overlap, h_split2 + h_overlap, height),
|
||||
'row5_col3': (h_split2 - h_overlap, v_split4 - v_overlap, h_split3 + h_overlap, height),
|
||||
'row5_col4': (h_split3 - h_overlap, v_split4 - v_overlap, h_split4 + h_overlap, height),
|
||||
'row5_col5': (h_split4 - h_overlap, v_split4 - v_overlap, width, height)
|
||||
}
|
||||
|
||||
# Crop and save each sector
|
||||
for sector_name, bbox in sectors.items():
|
||||
sector_img = img.crop(bbox)
|
||||
|
||||
output_filename = f"{png_file.stem}_{sector_name}.png"
|
||||
output_file = output_path / output_filename
|
||||
|
||||
# Save with optimized compression
|
||||
sector_img.save(
|
||||
output_file,
|
||||
'PNG',
|
||||
optimize=True,
|
||||
compress_level=6
|
||||
)
|
||||
|
||||
sector_width = bbox[2] - bbox[0]
|
||||
sector_height = bbox[3] - bbox[1]
|
||||
print(f" Saved: {output_filename} ({sector_width}x{sector_height}px)")
|
||||
|
||||
print(f"✓ Completed: {png_file.name} (25 sectors)")
|
||||
|
||||
except Exception as e:
|
||||
print(f"✗ Error processing {png_file.name}: {str(e)}")
|
||||
|
||||
print(f"\n{'='*50}")
|
||||
print(f"Splitting complete!")
|
||||
print(f"Output directory: {output_path.absolute()}")
|
||||
if __name__ == "__main__":
|
||||
# Example usage
|
||||
|
||||
# Step 1: Convert PDFs to PNGs
|
||||
input_directory = "./02_Fluxogramas" # Change this to your PDF directory
|
||||
output_directory = "./pngs" # Optional: specify output directory
|
||||
|
||||
# Convert with high DPI (300 is standard for print quality)
|
||||
#convert_pdfs_to_png(input_directory, output_directory, dpi=300)
|
||||
|
||||
# Step 2: Split PNGs into 9 sectors (3x3 grid)
|
||||
split_images_into_sectors("./pngs", "./sectors", overlap_percent=1)
|
||||
Reference in New Issue
Block a user