Удаление кадра/снаружи текста с точечного дисплея красного светодиодаC#

Место общения программистов C#
Ответить
Anonymous
 Удаление кадра/снаружи текста с точечного дисплея красного светодиода

Сообщение Anonymous »

Я строю приложение Maui, и мне нужно предварительно обработать фотографии красного пунктирного светодиодного дисплея, чтобы OCR мог прочитать цифры. Фотографии часто включают в себя рамку/раму, отверстия для светодиодов. Я должен удалить этот шум (верхний/правый/левый/снизу в зависимости от фотографии), сохраняя каждую светодиодную точку. Многие попытки предварительной обработки либо оставляют раму /винты, либо уничтожают точечную сетку. Ниже приведен текущий класс ImageSharp, который я использую, он примерно следует:
greyscale → Blur → обнаружение края → Большое дилатация → порог → инвертирование. Этот трубопровод не может правильно удалить шум. < /P>

Код: Выделить всё

 public class ImageSharpOcrProcessor
{
public static byte[] ProcessImageForOcr( string inputImagePath, byte binaryThreshold = 64 )
{

// Load image

using var image = Image.Load( inputImagePath );

return ProcessImageInternal( image, binaryThreshold );
}

public static async Task ProcessImageForOcrAsync( string inputImagePath, byte binaryThreshold = 64 )
{
return await Task.Run( () => ProcessImageForOcr( inputImagePath, binaryThreshold ) );
}

private static byte[] ProcessImageInternal( Image image, byte binaryThreshold )
{
// Convert to grayscale
image.Mutate( x => x.Grayscale() );

// Gaussian blur (5x5 kernel, sigma = 0 means auto-calculate)
image.Mutate( x => x.GaussianBlur( 2.0f ) ); // radius ~2 approximates 5x5 kernel

// Canny edge detection (ImageSharp doesn't have built-in Canny, so we'll use edge detection)
//image.Mutate( x => x.DetectEdges( KnownEdgeDetectorKernels.Sobel ) );
//alternative
ApplyCannyLikeEdgeDetection( image, 20f, 80f );

// Dilate (ImageSharp doesn't have built-in morphological operations, so we'll implement)
for ( int i = 0; i < 4; i++ ) // Apply dilation twice for stronger effect
ApplyDilation( image, 4 );

// Apply binary threshold before inversion (best practice for OCR)
ApplyBinaryThreshold( image, binaryThreshold ); // Convert gray pixels to pure black/white

// Bitwise NOT (invert)
image.Mutate( x => x.Invert() );

// Encode to PNG bytes
using var memoryStream = new MemoryStream();
image.Save( memoryStream, new PngEncoder() );

return memoryStream.ToArray();
}

// Alternative enhanced edge detection method (closer to Canny) - OPTIMIZED
private static void ApplyCannyLikeEdgeDetection( Image image, float lowThreshold = 50f, float highThreshold = 150f )
{
// Apply Sobel edge detection directly (skip extra Gaussian blur since we already did it)
image.Mutate( x => x.DetectEdges( KnownEdgeDetectorKernels.Sobel ) );

// Apply simplified threshold for better OCR results - OPTIMIZED
image.ProcessPixelRows( accessor =>
{
for( int y = 0 ; y < accessor.Height ; y++ )
{
var pixelRow = accessor.GetRowSpan( y );
for( int x = 0 ; x < pixelRow.Length ; x++ )
{
var pixel = pixelRow[ x ];
// Use only R channel since image is already grayscale (faster than averaging RGB)
float intensity = pixel.R;

// Simplified threshold: strong edges become white, everything else black
// We'll let the binary threshold step handle final cleanup
byte newValue = intensity > lowThreshold ? (byte)255 : (byte)0;

pixelRow[ x ] = new Rgba32( newValue, newValue, newValue, 255 );
}
}
} );
}

// Custom dilation implementation for SixLabors.ImageSharp - OPTIMIZED
private static void ApplyDilation( Image image, int kernelSize )
{
int width = image.Width;
int height = image.Height;
var originalPixels = new byte[ width * height ]; // Use 1D array for better performance

// First pass: copy original pixels (only R channel since grayscale)
image.ProcessPixelRows( accessor =>
{
for( int y = 0 ; y < height ; y++ )
{
var rowSpan = accessor.GetRowSpan( y );
for( int x = 0 ; x < width ; x++ )
{
originalPixels[ y * width + x ] = rowSpan[ x ].R;
}
}
} );

int halfKernel = kernelSize / 2;

// Apply dilation with optimized kernel
image.ProcessPixelRows( accessor =>
{
for( int y = 0 ; y < height ; y++ )
{
var pixelRow = accessor.GetRowSpan( y );
for( int x = 0 ; x < width ;  x++ )
{
byte maxValue = 0;

// Optimized kernel bounds
int startY = Math.Max( 0, y - halfKernel );
int endY = Math.Min( height - 1, y + halfKernel );
int startX = Math.Max( 0, x - halfKernel );
int endX = Math.Min( width - 1, x + halfKernel );

// Check kernel area with optimized indexing
for( int ny = startY ; ny 
{
for( int y = 0 ; y < accessor.Height ; y++ )
{
var pixelRow = accessor.GetRowSpan( y );
for( int x = 0 ; x < pixelRow.Length ; x++ )
{
var pixel = pixelRow[ x ];
// Use R channel since image is grayscale
byte intensity = pixel.R;

// Apply binary threshold: anything above threshold becomes white (255), below becomes black (0)
byte newValue = intensity > threshold ? (byte)255 : (byte)0;

pixelRow[ x ] = new Rgba32( newValue, newValue, newValue, 255 );
}
}
} );
}
}
Образец ввода/вывод
Входное изображение (RAW):

current output (what my pipine. />
Я не могу полагаться на фиксированную культуру (я ранее вырезал 25% от топа), потому что изображения различаются на расстоянии/угла/освещение. → Sobel/Edge → Dilation → Threshold и т. Д.), Но они либо оставляют раму, либо уничтожают сетку светодиодной точки.
Я хочу надежный метод (или короткий Python/OpenCV Prify-of-Concept I Can Port), который автоматически удаляет Bezel/Frame/Vints с любой стороны
(верхний/правый/внизу/внизу), пока не удаляет все светодиодные. /> И я также пытаюсь оставить, чтобы выпрямить текст. Но нет успеха и

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

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

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

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

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

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