Устойчивое обнаружение и обрезка визитных карточек с OpenCVSharp в C# (сбое на низкоконтрастных и текстурированных фонахC#

Место общения программистов C#
Ответить
Anonymous
 Устойчивое обнаружение и обрезка визитных карточек с OpenCVSharp в C# (сбое на низкоконтрастных и текстурированных фонах

Сообщение Anonymous »

Я разрабатываю функцию в приложении .NET 8 для автоматического обнаружения, точки зрения и обрезки визитной карточки с карты, загруженной пользователем, с использованием библиотеки OpenCVSharp4. Тем не менее, он постоянно терпит неудачу в более реалистичных, сложных ситуациях.
Проблема:
Алгоритм не может найти правильный контур визитной карточки в двух основных случаях:
Недовопроводный фон: например, белая визитная карта на светоцветной стойке. Например, визитная карточка в узорной таблице PlaceMat или деревянной зернистости. (План А и план б) найти карту. Код структурирован для сохранения исходного изображения, так и (попыток) обрезанного изображения. < /P>
Вот полный метод C#: < /p>
// using OpenCvSharp;
// using SixLabors.ImageSharp;
// ...and other necessary using statements at the top of the file.

public static async Task SaveBusinessCardAsync(IFormFile businessCardFile)
{
if (businessCardFile == null || !businessCardFile.ContentType.StartsWith("image"))
{
return (null, null);
}

try
{
// Prep paths and save the original file (unchanged)
string fileStorageRoot = AppSettingsHelper.GetSystemConfigValue("strFileServer");
string folderFullPath = Path.Combine(fileStorageRoot, "Upload_Address", "BusinessCards");
if (!Directory.Exists(folderFullPath)) Directory.CreateDirectory(folderFullPath);
string originalFileName = businessCardFile.FileName;
string guid = Guid.NewGuid().ToString();
string originalSavePath = Path.Combine(folderFullPath, $"{{{guid}}}_orig.jpg");
string croppedSavePath = Path.Combine(folderFullPath, $"{{{guid}}}_crop.jpg");

using (var image = await SixLabors.ImageSharp.Image.LoadAsync(businessCardFile.OpenReadStream()))
{
await image.SaveAsJpegAsync(originalSavePath, new JpegEncoder { Quality = 90 });
}

using (var stream = businessCardFile.OpenReadStream())
using (Mat originalImage = Mat.FromStream(stream, ImreadModes.Color))
{
if (originalImage.Empty()) throw new Exception("OpenCV could not read the image file.");

// Pre-resize the image for faster and more stable processing
double scale = 1000.0 / originalImage.Width;
var newSize = new OpenCvSharp.Size(1000, (int)(originalImage.Height * scale));
using Mat resizedImage = new Mat();
Cv2.Resize(originalImage, resizedImage, newSize);

OpenCvSharp.Point[] largestContour = null;

// --- Plan A: Canny Edge Detection Based Approach ---
using (Mat grayImage = new Mat())
using (Mat blurredImage = new Mat())
using (Mat edges = new Mat())
using (Mat kernel = Cv2.GetStructuringElement(MorphShapes.Rect, new OpenCvSharp.Size(7, 7)))
using (Mat closedEdges = new Mat())
{
Cv2.CvtColor(resizedImage, grayImage, ColorConversionCodes.BGR2GRAY);
Cv2.GaussianBlur(grayImage, blurredImage, new OpenCvSharp.Size(5, 5), 0);

double median = Cv2.Mean(blurredImage).Val0;
double lowerThreshold = Math.Max(0, 0.7 * median);
double upperThreshold = Math.Min(255, 1.3 * median);
Cv2.Canny(blurredImage, edges, lowerThreshold, upperThreshold);

Cv2.MorphologyEx(edges, closedEdges, MorphTypes.Close, kernel, iterations: 3);

OpenCvSharp.Point[][] contours;
HierarchyIndex[] hierarchy;
Cv2.FindContours(closedEdges, out contours, out hierarchy, RetrievalModes.External, ContourApproximationModes.ApproxSimple);

double imageArea = resizedImage.Width * resizedImage.Height;
var sortedCandidates = contours
.Select(c => Cv2.ApproxPolyDP(c, Cv2.ArcLength(c, true) * 0.02, true))
.Where(poly =>
{
if (poly.Length != 4) return false;
double area = Cv2.ContourArea(poly);
if (area < imageArea * 0.10) return false;

var rect = Cv2.BoundingRect(poly);
double aspectRatio = (double)rect.Width / rect.Height;
return aspectRatio > 1.2 && aspectRatio < 2.2;
})
.OrderByDescending(c => Cv2.ContourArea(c))
.ToList();

if (sortedCandidates.Any())
{
var firstCandidate = sortedCandidates[0];
if (Cv2.ContourArea(firstCandidate) < imageArea * 0.95)
{
largestContour = firstCandidate;
}
else if (sortedCandidates.Count > 1)
{
largestContour = sortedCandidates[1];
}
}
}

// --- Plan B: Text/Content Block Based Approach (if Plan A fails) ---
if (largestContour == null)
{
using Mat grayForText = new Mat();
Cv2.CvtColor(resizedImage, grayForText, ColorConversionCodes.BGR2GRAY);

using Mat thresh = new Mat();
Cv2.AdaptiveThreshold(grayForText, thresh, 255, AdaptiveThresholdTypes.GaussianC, ThresholdTypes.BinaryInv, 11, 2);

using Mat kernel = Cv2.GetStructuringElement(MorphShapes.Rect, new OpenCvSharp.Size(15, 3));
using Mat morph = new Mat();
Cv2.MorphologyEx(thresh, morph, MorphTypes.Close, kernel, iterations: 2);

OpenCvSharp.Point[][] textContours;
HierarchyIndex[] hierarchy;
Cv2.FindContours(morph, out textContours, out hierarchy, RetrievalModes.External, ContourApproximationModes.ApproxSimple);

if (textContours.Length > 0)
{
var allPoints = textContours.SelectMany(c => c);
Rect boundingRect = Cv2.BoundingRect(allPoints);

largestContour = new OpenCvSharp.Point[] {
new OpenCvSharp.Point(boundingRect.X, boundingRect.Y),
new OpenCvSharp.Point(boundingRect.X + boundingRect.Width, boundingRect.Y),
new OpenCvSharp.Point(boundingRect.X + boundingRect.Width, boundingRect.Y + boundingRect.Height),
new OpenCvSharp.Point(boundingRect.X, boundingRect.Y + boundingRect.Height)
};
}
}

Mat croppedImage = null;
if (largestContour != null)
{
// Rescale points back to the original image for high-quality crop
Point2f[] sourcePoints = largestContour.Select(p => new Point2f((float)(p.X / scale), (float)(p.Y / scale))).ToArray();
// ... (Perspective transform logic is here) ...
}

Mat imageToSave = croppedImage ?? originalImage;

// ... (Saving logic is here) ...

croppedImage?.Dispose();

return (FilePath: croppedSavePath, FileName: originalFileName);
}
}
catch (Exception ex)
{
// ... logging and error handling
return (null, null);
}
}
< /code>
Мой вопрос: < /p>
Даже с этим двухплановым подходом я все еще не могу получить надежный культ на примере изображений. Кажется, что мой алгоритм недостаточно надежна. Например: < /p>
Существуют ли лучшие методы предварительной обработки для улучшения слабых ребра на изображениях с низким содержанием контрасти? Алгоритмические подходы были бы очень оценены.
Спасибо!

Подробнее здесь: https://stackoverflow.com/questions/797 ... rp-failing
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «C#»