Amazon Rekognition - Комп'ютерний зір
Amazon Rekognition - Комп'ютерний зір
Amazon Rekognition — це хмарний сервіс комп'ютерного зору (Computer Vision), який працює на базі готових навчених моделей глибокого навчання від AWS. Він не потребує від розробника експертизи в Data Science. Ви передаєте зображення (як потік байтів або посилання на об'єкт в S3) і отримуєте структуровану JSON-відповідь про об'єкти, обличчя, тексти та сцени.
Можливості Amazon Rekognition
Детекція об'єктів та сцен (Labels)
Label) із рівнем впевненості (Confidence) та координатами об'єктів (BoundingBox). Наприклад: Car (99%), Tree (95%).Аналіз та порівняння облич
Розпізнавання тексту (OCR)
Модерація контенту
Реалізація сервісу на .NET 8
Для роботи встановіть NuGet-пакет:
dotnet add package AWSSDK.Rekognition
Нижче наведено повний та готовий до використання клас сервісу RekognitionService.cs зі всіма DTO та методами для обробки зображень.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Amazon.Rekognition;
using Amazon.Rekognition.Model;
namespace AwsAiPlayground.Services;
// DTO для об'єктів
public record BoundingBoxDto(float Left, float Top, float Width, float Height);
public record DetectedLabelDto(
string Name,
float Confidence,
List<string> Categories,
BoundingBoxDto? BoundingBox);
// DTO для модерації
public record ModerationViolationDto(string Category, string SubCategory, float Confidence);
public record ContentModerationResultDto(
bool IsSafe,
List<ModerationViolationDto> Violations);
// DTO для облич
public record FaceDetailDto(
string Gender,
string AgeRange,
string Emotion,
float Confidence,
BoundingBoxDto BoundingBox);
public record FaceComparisonResultDto(
float Similarity,
BoundingBoxDto TargetBoundingBox);
public sealed class RekognitionService
{
private readonly IAmazonRekognition _rekognitionClient;
public RekognitionService(IAmazonRekognition rekognitionClient)
{
_rekognitionClient = rekognitionClient;
}
/// <summary>
/// Детектує об'єкти на зображенні з S3.
/// </summary>
public async Task<List<DetectedLabelDto>> DetectLabelsAsync(
string bucketName,
string imageKey,
float minConfidence = 70f)
{
var request = new DetectLabelsRequest
{
Image = new Image
{
S3Object = new S3Object { Bucket = bucketName, Name = imageKey }
},
MaxLabels = 50,
MinConfidence = minConfidence
};
try
{
var response = await _rekognitionClient.DetectLabelsAsync(request);
return response.Labels.Select(l => new DetectedLabelDto(
Name: l.Name,
Confidence: l.Confidence,
Categories: l.Categories.Select(c => c.Name).ToList(),
BoundingBox: l.Instances.FirstOrDefault()?.BoundingBox is { } bb
? new BoundingBoxDto(bb.Left, bb.Top, bb.Width, bb.Height)
: null
)).ToList();
}
catch (AmazonRekognitionException ex)
{
throw new Exception($"Error calling Amazon Rekognition DetectLabels: {ex.Message}", ex);
}
}
/// <summary>
/// Перевіряє зображення на наявність небажаного вмісту.
/// </summary>
public async Task<ContentModerationResultDto> ModerateImageAsync(string bucketName, string imageKey)
{
var request = new DetectModerationLabelsRequest
{
Image = new Image
{
S3Object = new S3Object { Bucket = bucketName, Name = imageKey }
},
MinConfidence = 50f
};
try
{
var response = await _rekognitionClient.DetectModerationLabelsAsync(request);
var violations = response.ModerationLabels.Select(ml => new ModerationViolationDto(
Category: ml.ParentName ?? ml.Name,
SubCategory: ml.Name,
Confidence: ml.Confidence
)).ToList();
return new ContentModerationResultDto(
IsSafe: violations.Count == 0,
Violations: violations
);
}
catch (AmazonRekognitionException ex)
{
throw new Exception($"Error calling Amazon Rekognition ModerateImage: {ex.Message}", ex);
}
}
/// <summary>
/// OCR розпізнавання тексту на фотографії з S3.
/// </summary>
public async Task<string> ExtractTextAsync(string bucketName, string imageKey)
{
var request = new DetectTextRequest
{
Image = new Image
{
S3Object = new S3Object { Bucket = bucketName, Name = imageKey }
}
};
try
{
var response = await _rekognitionClient.DetectTextAsync(request);
// Фільтруємо лише цілі рядки (LINES), ігноруючи окремі слова
var lines = response.TextDetections
.Where(t => t.Type == TextTypes.LINE)
.OrderBy(t => t.Geometry.BoundingBox.Top)
.Select(t => t.DetectedText);
return string.Join("\n", lines);
}
catch (AmazonRekognitionException ex)
{
throw new Exception($"Error calling Amazon Rekognition DetectText: {ex.Message}", ex);
}
}
/// <summary>
/// Порівнює обличчя на джерельному фото з обличчями на цільовому фото.
/// </summary>
public async Task<List<FaceComparisonResultDto>> CompareFacesAsync(
string sourceBucket, string sourceKey,
string targetBucket, string targetKey,
float similarityThreshold = 80f)
{
var request = new CompareFacesRequest
{
SourceImage = new Image { S3Object = new S3Object { Bucket = sourceBucket, Name = sourceKey } },
TargetImage = new Image { S3Object = new S3Object { Bucket = targetBucket, Name = targetKey } },
SimilarityThreshold = similarityThreshold
};
try
{
var response = await _rekognitionClient.CompareFacesAsync(request);
return response.FaceMatches.Select(m => new FaceComparisonResultDto(
Similarity: m.Similarity,
TargetBoundingBox: new BoundingBoxDto(
m.Face.BoundingBox.Left,
m.Face.BoundingBox.Top,
m.Face.BoundingBox.Width,
m.Face.BoundingBox.Height)
)).ToList();
}
catch (AmazonRekognitionException ex)
{
throw new Exception($"Error calling Amazon Rekognition CompareFaces: {ex.Message}", ex);
}
}
}
Інтеграція з React: Візуалізація Bounding Boxes на Canvas
Для того щоб відобразити результати виявлення Rekognition (об'єкти, обличчя) поверх оригінального фото, координати Bounding Box перераховуються у пікселі та малюються на елементі <canvas>.
Координати від AWS Rekognition є відносними (значення від 0 до 1 відносно ширини та висоти зображення).
Повний React компонент ImageAnalyzer
import React, { useRef, useState, useEffect } from 'react';
export interface BoundingBox {
left: number;
top: number;
width: number;
height: number;
}
export interface DetectedLabel {
name: string;
confidence: number;
boundingBox?: BoundingBox | null;
}
interface ImageAnalyzerProps {
imageUrl: string;
labels: DetectedLabel[];
}
export function ImageAnalyzer({ imageUrl, labels }: ImageAnalyzerProps) {
const canvasRef = useRef<HTMLCanvasElement>(null);
const imageRef = useRef<HTMLImageElement | null>(null);
const [imgLoaded, setImgLoaded] = useState(false);
useEffect(() => {
// Створюємо та завантажуємо зображення
const img = new Image();
img.src = imageUrl;
img.crossOrigin = 'anonymous'; // дозволяє читання пікселів при CORS
img.onload = () => {
imageRef.current = img;
setImgLoaded(true);
};
img.onerror = () => {
console.error('Не вдалося завантажити зображення.');
};
}, [imageUrl]);
useEffect(() => {
if (!imgLoaded || !imageRef.current || !canvasRef.current) return;
const canvas = canvasRef.current;
const ctx = canvas.getContext('2d');
if (!ctx) return;
const img = imageRef.current;
// Встановлюємо розміри canvas відповідно до реальних розмірів зображення
canvas.width = img.naturalWidth;
canvas.height = img.naturalHeight;
// Малюємо оригінальне зображення
ctx.drawImage(img, 0, 0);
// Малюємо Bounding Boxes для кожного об'єкта
labels.forEach((label) => {
if (!label.boundingBox) return;
const { left, top, width, height } = label.boundingBox;
// Конвертуємо відносні координати (0..1) в пікселі
const x = left * canvas.width;
const y = top * canvas.height;
const w = width * canvas.width;
const h = height * canvas.height;
// Налаштовуємо стилі рамки
ctx.strokeStyle = '#00ffcc';
ctx.lineWidth = Math.max(3, canvas.width * 0.003); // товщина рамки адаптується під роздільну здатність
ctx.strokeRect(x, y, w, h);
// Малюємо плашку для тексту
ctx.fillStyle = 'rgba(0, 255, 204, 0.85)';
const fontSize = Math.max(14, canvas.width * 0.015);
ctx.font = `bold ${fontSize}px sans-serif`;
const text = `${label.name} (${Math.round(label.confidence)}%)`;
const textWidth = ctx.measureText(text).width;
const padding = 6;
// Запобігаємо виходу плашки за верхній край
const labelY = y - fontSize - padding > 0 ? y - fontSize - padding : y;
ctx.fillRect(x, labelY, textWidth + padding * 2, fontSize + padding);
// Малюємо сам текст
ctx.fillStyle = '#000000';
ctx.fillText(text, x + padding, labelY + fontSize);
});
}, [imgLoaded, labels]);
return (
<div style={styles.container}>
<div style={styles.canvasWrapper}>
<canvas ref={canvasRef} style={styles.canvas} />
</div>
<div style={styles.detailsList}>
<h3 style={styles.listTitle}>Виявлені об'єкти:</h3>
<ul style={styles.list}>
{labels.map((l, index) => (
<li key={index} style={styles.listItem}>
<span style={styles.labelName}>{l.name}</span>
<span style={styles.confidenceBadge}>{Math.round(l.confidence)}%</span>
</li>
))}
</ul>
</div>
</div>
);
}
const styles = {
container: {
display: 'flex',
flexDirection: 'row' as const,
gap: '24px',
background: '#1f2937',
padding: '24px',
borderRadius: '12px',
boxShadow: '0 4px 20px rgba(0, 0, 0, 0.3)',
color: '#f3f4f6',
flexWrap: 'wrap' as const,
},
canvasWrapper: {
flex: '2 1 500px',
position: 'relative' as const,
background: '#111827',
borderRadius: '8px',
overflow: 'hidden',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
},
canvas: {
maxWidth: '100%',
maxHeight: '550px',
objectFit: 'contain' as const,
},
detailsList: {
flex: '1 1 250px',
background: '#111827',
padding: '16px',
borderRadius: '8px',
border: '1px solid rgba(255, 255, 255, 0.05)',
},
listTitle: {
marginTop: 0,
borderBottom: '1px solid rgba(255, 255, 255, 0.1)',
paddingBottom: '8px',
color: '#60a5fa',
},
list: {
listStyleType: 'none',
padding: 0,
margin: 0,
maxHeight: '400px',
overflowY: 'auto' as const,
},
listItem: {
display: 'flex',
justifyContent: 'space-between',
padding: '8px 4px',
borderBottom: '1px solid rgba(255, 255, 255, 0.03)',
},
labelName: {
fontWeight: 500,
},
confidenceBadge: {
background: '#10b981',
color: '#fff',
padding: '2px 8px',
borderRadius: '12px',
fontSize: '0.85rem',
},
};
Amazon Bedrock - Foundation Models, RAG та Agents
Глибоке занурення в Amazon Bedrock. Практична інтеграція великих мовних моделей Claude, Llama та Titan з .NET 8 та React. Реалізація Streaming, побудова Knowledge Bases для RAG, налаштування Guardrails безпеки та розробка автономних AI Agents.
Amazon Textract - Інтелектуальний аналіз документів
Практичний посібник з використання Amazon Textract. Повна реалізація аналізу форм та таблиць з PDF/зображень у .NET 8. Клієнтський React-компонент для відображення структур документів.