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
Мобильная версия