Initial work on new OCR line splitter

This commit is contained in:
Nikolaj Olsson 2020-06-05 10:42:49 +02:00
parent eefea2601d
commit 34102f0dac
4 changed files with 325 additions and 115 deletions

Binary file not shown.

View File

@ -1344,6 +1344,18 @@ namespace Nikse.SubtitleEdit.Core
return true;
}
public bool IsImageOnlyTransparent()
{
for (int i = 0; i < _bitmapData.Length; i += 4)
{
if (_bitmapData[i] != 0) // check alpha
{
return false;
}
}
return true;
}
public void EnsureEvenLines(Color fillColor)
{
if (Width % 2 == 0 && Height % 2 == 0)

View File

@ -6368,8 +6368,6 @@ namespace Nikse.SubtitleEdit.Forms.Ocr
_selectedIndex = -1;
textBoxCurrentText.Text = string.Empty;
}
MakeHorizontalSplit();
}
private void SelectedIndexChangedAction()
@ -6590,116 +6588,6 @@ namespace Nikse.SubtitleEdit.Forms.Ocr
e.SuppressKeyPress = true;
MakeHorizontalSplit();
}
else if (e.Modifiers == (Keys.Control | Keys.Shift) && e.KeyCode == Keys.H && (_ocrMethodIndex == _ocrMethodBinaryImageCompare || _ocrMethodIndex == _ocrMethodNocr))
{
e.SuppressKeyPress = true;
var random = new Random();
var bmp = (Bitmap)pictureBoxSubtitleImage.Image;
if (bmp != null)
{
var nBmp = new NikseBitmap(bmp);
bmp.Dispose();
bool started = false;
for (int y = 12; y < nBmp.Height - 15; y++)
{
var points = new List<Point>();
var yChange = 0;
var c = Color.FromArgb(255, random.Next(200) + 55, random.Next(200) + 55, random.Next(200) + 55);
var completed = false;
int x = nBmp.Width - 1;
while (x > 0)
{
var a1 = nBmp.GetAlpha(x, y + yChange);
var a2 = nBmp.GetAlpha(x, y + 1 + yChange);
if (a1 > 150 || a2 > 150)
{
if (x > 1 && yChange < 8 &&
nBmp.GetAlpha(x + 1, y + yChange) < 150 && nBmp.GetAlpha(x + 1, y + yChange) < 150 &&
nBmp.GetAlpha(x + 1, y + 1 + yChange) < 150 && nBmp.GetAlpha(x + 1, y + 2 + yChange) < 150 &&
nBmp.GetAlpha(x + 1, y + 2 + yChange) < 150 && nBmp.GetAlpha(x + 1, y + 3 + yChange) < 150 &&
nBmp.GetAlpha(x, y + 2 + yChange) < 150 && nBmp.GetAlpha(x, y + 3 + yChange) < 150)
{
yChange += 2;
}
else if (x > 1 && yChange < 8 &&
nBmp.GetAlpha(x + 1, y + yChange) < 150 && nBmp.GetAlpha(x + 1, y + yChange) < 150 &&
nBmp.GetAlpha(x + 1, y + 1 + yChange) < 150 && nBmp.GetAlpha(x + 1, y + 2 + yChange) < 150 &&
nBmp.GetAlpha(x + 1, y + 2 + yChange) < 150 && nBmp.GetAlpha(x + 1, y + 3 + yChange) < 150 &&
nBmp.GetAlpha(x + 1, y + 3 + yChange) < 150 && nBmp.GetAlpha(x + 1, y + 4 + yChange) < 150 &&
nBmp.GetAlpha(x, y + 3 + yChange) < 150 && nBmp.GetAlpha(x, y + 4 + yChange) < 150)
{
yChange += 3;
}
else if (x > 1 && yChange < 7 &&
nBmp.GetAlpha(x + 1, y + yChange) < 150 && nBmp.GetAlpha(x + 1, y + yChange) < 150 &&
nBmp.GetAlpha(x + 1, y + 1 + yChange) < 150 && nBmp.GetAlpha(x + 1, y + 2 + yChange) < 150 &&
nBmp.GetAlpha(x + 1, y + 2 + yChange) < 150 && nBmp.GetAlpha(x + 1, y + 3 + yChange) < 150 &&
nBmp.GetAlpha(x + 1, y + 3 + yChange) < 150 && nBmp.GetAlpha(x + 1, y + 4 + yChange) < 150 &&
nBmp.GetAlpha(x + 1, y + 4 + yChange) < 150 && nBmp.GetAlpha(x + 1, y + 5 + yChange) < 150 &&
nBmp.GetAlpha(x, y + 4 + yChange) < 150 && nBmp.GetAlpha(x, y + 5 + yChange) < 150)
{
yChange += 4;
}
else if (x > 1 && yChange < 8 &&
nBmp.GetAlpha(x + 1, y + yChange) < 150 && nBmp.GetAlpha(x + 1, y + yChange) < 150 &&
nBmp.GetAlpha(x + 1, y - 1 + yChange) < 150 && nBmp.GetAlpha(x + 1, y - 2 + yChange) < 150 &&
nBmp.GetAlpha(x + 1, y - 2 + yChange) < 150 && nBmp.GetAlpha(x + 1, y - 3 + yChange) < 150 &&
nBmp.GetAlpha(x, y - 2 + yChange) < 150 && nBmp.GetAlpha(x, y - 3 + yChange) < 150)
{
yChange -= 2;
}
else if (x > 1 && yChange < 8 &&
nBmp.GetAlpha(x + 1, y + yChange) < 150 && nBmp.GetAlpha(x + 1, y + yChange) < 150 &&
nBmp.GetAlpha(x + 1, y - 1 + yChange) < 150 && nBmp.GetAlpha(x + 1, y - 2 + yChange) < 150 &&
nBmp.GetAlpha(x + 1, y - 2 + yChange) < 150 && nBmp.GetAlpha(x + 1, y - 3 + yChange) < 150 &&
nBmp.GetAlpha(x + 1, y - 3 + yChange) < 150 && nBmp.GetAlpha(x + 1, y - 4 + yChange) < 150 &&
nBmp.GetAlpha(x, y - 3 + yChange) < 150 && nBmp.GetAlpha(x, y - 4 + yChange) < 150)
{
yChange -= 3;
}
else if (x > 1 && yChange < 7 &&
nBmp.GetAlpha(x + 1, y + yChange) < 150 && nBmp.GetAlpha(x + 1, y + yChange) < 150 &&
nBmp.GetAlpha(x + 1, y - 1 + yChange) < 150 && nBmp.GetAlpha(x + 1, y - 2 + yChange) < 150 &&
nBmp.GetAlpha(x + 1, y - 2 + yChange) < 150 && nBmp.GetAlpha(x + 1, y - 3 + yChange) < 150 &&
nBmp.GetAlpha(x + 1, y - 3 + yChange) < 150 && nBmp.GetAlpha(x + 1, y - 4 + yChange) < 150 &&
nBmp.GetAlpha(x + 1, y - 4 + yChange) < 150 && nBmp.GetAlpha(x + 1, y - 5 + yChange) < 150 &&
nBmp.GetAlpha(x, y - 4 + yChange) < 150 && nBmp.GetAlpha(x, y - 5 + yChange) < 150)
{
yChange -= 4;
}
else
{
started = true;
break;
}
}
if (started)
{
points.Add(new Point(x, y + yChange));
}
completed = x <= 1;
x--;
}
if (completed)
{
foreach (var point in points)
{
nBmp.SetPixel(point.X, point.Y, c);
}
}
}
pictureBoxSubtitleImage.Image = nBmp.GetBitmap();
}
}
}
private void MakeHorizontalSplit()
@ -6711,7 +6599,7 @@ namespace Nikse.SubtitleEdit.Forms.Ocr
var nBmp = new NikseBitmap(bmp);
bmp.Dispose();
bool started = false;
Dictionary<int, List<Point>> splitLines = new Dictionary<int, List<Point>>();
var splitLines = new Dictionary<int, List<Point>>();
for (int y = 12; y < nBmp.Height - 15; y++)
{
var points = new List<Point>();
@ -6854,7 +6742,7 @@ namespace Nikse.SubtitleEdit.Forms.Ocr
}
}
textBoxCurrentText.Text = $"{straightLines} straight horizontal lines";
Text = $"{straightLines} straight horizontal lines";
pictureBoxSubtitleImage.Image = nBmp.GetBitmap();
}

View File

@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
namespace Nikse.SubtitleEdit.Logic
{
@ -428,7 +429,17 @@ namespace Nikse.SubtitleEdit.Logic
var list = new List<ImageSplitterItem>();
// split into separate lines
var lineBitmaps = SplitToLines(bmp, minLineHeight, averageLineHeight);
var lineBitmaps = new List<ImageSplitterItem>();
//OLD: lineBitmaps = SplitToLines(bmp, minLineHeight, averageLineHeight);
// fast 3-x-blank-horizontal-lines split
var tempBitmaps = SplitToLinesByMinTransparentHorizontalLines(bmp, minLineHeight, 3);
foreach (var bitmap in tempBitmaps)
{
var bitmaps = SplitToLinesNew(bitmap, minLineHeight, averageLineHeight); // more advanced split (allows for up/down)
lineBitmaps.AddRange(bitmaps);
}
if (!topToBottom)
{
@ -461,6 +472,305 @@ namespace Nikse.SubtitleEdit.Logic
return list;
}
/// <summary>
/// split into lines
/// </summary>
public static List<ImageSplitterItem> SplitToLinesByMinTransparentHorizontalLines(NikseBitmap bmp, int minLineHeight, int minTransparentLines)
{
var parts = new List<ImageSplitterItem>();
var startY = 0;
var lastTransparentY = -1;
var keysInSequence = 0;
for (int y = minLineHeight; y < bmp.Height - minLineHeight; y++)
{
var isLineTransparent = bmp.IsLineTransparent(y);
if (startY == y && isLineTransparent)
{
startY++;
continue; // skip start
}
if (isLineTransparent)
{
if (lastTransparentY == y - 1)
{
if (keysInSequence == 0)
{
keysInSequence++;
}
keysInSequence++;
}
if (keysInSequence > 2 && lastTransparentY - startY > minLineHeight)
{
var part = bmp.CopyRectangle(new Rectangle(0, startY, bmp.Width, lastTransparentY - startY - 1));
if (!part.IsImageOnlyTransparent())
{
var croppedTop = part.CropTopTransparent(0);
parts.Add(new ImageSplitterItem(0, startY + croppedTop, part));
}
startY = lastTransparentY + 1;
}
lastTransparentY = y;
}
else
{
keysInSequence = 0;
lastTransparentY = -1;
}
}
if (bmp.Height - startY > minLineHeight)
{
var part = bmp.CopyRectangle(new Rectangle(0, startY, bmp.Width, bmp.Height - startY));
if (!part.IsImageOnlyTransparent())
{
var croppedTop = part.CropTopTransparent(0);
parts.Add(new ImageSplitterItem(0, startY + croppedTop, part));
}
}
return parts;
}
/// <summary>
/// split into lines
/// </summary>
public static List<ImageSplitterItem> SplitToLinesNew(ImageSplitterItem item, int minLineHeight, double averageLineHeight = -1)
{
var bmp = item.NikseBitmap;
var parts = new List<ImageSplitterItem>();
bool started = false;
var splitLines = new Dictionary<int, List<Point>>();
var startY = 0;
for (int y = minLineHeight; y < bmp.Height - minLineHeight; y++)
{
if (startY == y && bmp.IsLineTransparent(y))
{
startY++;
continue;
}
var points = new List<Point>();
var yChange = 0;
var completed = false;
var backJump = 0;
int x = 0;
while (x < bmp.Width)
{
var a1 = bmp.GetAlpha(x, y + yChange);
var a2 = bmp.GetAlpha(x, y + 1 + yChange);
if (a1 > 150 || a2 > 150)
{
if (x > 1 && yChange < 8 &&
bmp.GetAlpha(x - 1, y + yChange) < 150 && bmp.GetAlpha(x - 1, y + yChange) < 150 &&
bmp.GetAlpha(x - 1, y + 1 + yChange) < 150 && bmp.GetAlpha(x - 1, y + 2 + yChange) < 150 &&
bmp.GetAlpha(x - 1, y + 2 + yChange) < 150 && bmp.GetAlpha(x - 1, y + 3 + yChange) < 150 &&
bmp.GetAlpha(x, y + 2 + yChange) < 150 && bmp.GetAlpha(x, y + 3 + yChange) < 150)
{
yChange += 2;
}
else if (x > 1 && yChange < 8 &&
bmp.GetAlpha(x - 1, y + yChange) < 150 && bmp.GetAlpha(x - 1, y + yChange) < 150 &&
bmp.GetAlpha(x - 1, y + 1 + yChange) < 150 && bmp.GetAlpha(x - 1, y + 2 + yChange) < 150 &&
bmp.GetAlpha(x - 1, y + 2 + yChange) < 150 && bmp.GetAlpha(x - 1, y + 3 + yChange) < 150 &&
bmp.GetAlpha(x - 1, y + 3 + yChange) < 150 && bmp.GetAlpha(x - 1, y + 4 + yChange) < 150 &&
bmp.GetAlpha(x, y + 3 + yChange) < 150 && bmp.GetAlpha(x, y + 4 + yChange) < 150)
{
yChange += 3;
}
else if (x > 1 && yChange < 7 &&
bmp.GetAlpha(x - 1, y + yChange) < 150 && bmp.GetAlpha(x - 1, y + yChange) < 150 &&
bmp.GetAlpha(x - 1, y + 1 + yChange) < 150 && bmp.GetAlpha(x - 1, y + 2 + yChange) < 150 &&
bmp.GetAlpha(x - 1, y + 2 + yChange) < 150 && bmp.GetAlpha(x - 1, y + 3 + yChange) < 150 &&
bmp.GetAlpha(x - 1, y + 3 + yChange) < 150 && bmp.GetAlpha(x - 1, y + 4 + yChange) < 150 &&
bmp.GetAlpha(x - 1, y + 4 + yChange) < 150 && bmp.GetAlpha(x - 1, y + 5 + yChange) < 150 &&
bmp.GetAlpha(x, y + 4 + yChange) < 150 && bmp.GetAlpha(x, y + 5 + yChange) < 150)
{
yChange += 4;
}
else if (x > 1 && yChange > -7 &&
bmp.GetAlpha(x - 1, y + yChange) < 150 && bmp.GetAlpha(x - 1, y + yChange) < 150 &&
bmp.GetAlpha(x - 1, y - 1 + yChange) < 150 && bmp.GetAlpha(x - 1, y - 2 + yChange) < 150 &&
bmp.GetAlpha(x - 1, y - 2 + yChange) < 150 && bmp.GetAlpha(x - 1, y - 3 + yChange) < 150 &&
bmp.GetAlpha(x, y - 2 + yChange) < 150 && bmp.GetAlpha(x, y - 3 + yChange) < 150)
{
yChange -= 2;
}
else if (x > 1 && yChange > -7 &&
bmp.GetAlpha(x - 1, y + yChange) < 150 && bmp.GetAlpha(x - 1, y + yChange) < 150 &&
bmp.GetAlpha(x - 1, y - 1 + yChange) < 150 && bmp.GetAlpha(x - 1, y - 2 + yChange) < 150 &&
bmp.GetAlpha(x - 1, y - 2 + yChange) < 150 && bmp.GetAlpha(x - 1, y - 3 + yChange) < 150 &&
bmp.GetAlpha(x - 1, y - 3 + yChange) < 150 && bmp.GetAlpha(x - 1, y - 4 + yChange) < 150 &&
bmp.GetAlpha(x, y - 3 + yChange) < 150 && bmp.GetAlpha(x, y - 4 + yChange) < 150)
{
yChange -= 3;
}
else if (x > 1 && yChange > -7 &&
bmp.GetAlpha(x - 1, y + yChange) < 150 && bmp.GetAlpha(x - 1, y + yChange) < 150 &&
bmp.GetAlpha(x - 1, y - 1 + yChange) < 150 && bmp.GetAlpha(x - 1, y - 2 + yChange) < 150 &&
bmp.GetAlpha(x - 1, y - 2 + yChange) < 150 && bmp.GetAlpha(x - 1, y - 3 + yChange) < 150 &&
bmp.GetAlpha(x - 1, y - 3 + yChange) < 150 && bmp.GetAlpha(x - 1, y - 4 + yChange) < 150 &&
bmp.GetAlpha(x - 1, y - 4 + yChange) < 150 && bmp.GetAlpha(x - 1, y - 5 + yChange) < 150 &&
bmp.GetAlpha(x, y - 4 + yChange) < 150 && bmp.GetAlpha(x, y - 5 + yChange) < 150)
{
yChange -= 4;
}
else if (x > 10 && backJump < 3 && x > 5 && yChange > -7) // go left + up + check 12 pixels right
{
var done = false;
for (int i = 1; i < 15; i++)
{
for (int k = 1; k < 9; k++)
{
if (CanGoUpAndRight(bmp, i, 12, x - k, y + yChange))
{
backJump++;
x -= k;
points.RemoveAll(p => p.X > x);
done = true;
yChange -= (i + 1);
break;
}
}
if (done)
{
break;
}
}
if (!done)
{
started = true;
break;
}
}
else
{
started = true;
break;
}
}
if (started)
{
points.Add(new Point(x, y + yChange));
}
completed = x == bmp.Width - 1;
x++;
}
if (completed)
{
splitLines.Add(y, points);
}
}
var lastKey = -10;
var transparentColor = Color.FromArgb(0, 0, 0, 0);
foreach (var line in splitLines)
{
var key = line.Key;
if (key - startY > minLineHeight)
{
var maxY = line.Value.Max(p => p.Y);
var part = bmp.CopyRectangle(new Rectangle(0, startY, bmp.Width, maxY - startY));
//part.GetBitmap().Save(@"j:\temp\split_" + parts.Count + "_before.bmp");
foreach (var point in line.Value)
{
// delete down
for (var y = point.Y - 1; y < startY + part.Height; y++)
{
part.SetPixel(point.X, y - startY, transparentColor);
}
}
if (!part.IsImageOnlyTransparent())
{
var minY = line.Value.Min(p => p.Y);
// bmp.GetBitmap().Save(@"j:\temp\main_" + parts.Count + "_before.bmp");
foreach (var point in line.Value)
{
// delete up
for (var y = point.Y; y >= minY; y--)
{
bmp.SetPixel(point.X, y, transparentColor);
}
}
// bmp.GetBitmap().Save(@"j:\temp\main_" + parts.Count + "_after.bmp");
// part.GetBitmap().Save(@"j:\temp\split_" + parts.Count + "_after.bmp");
var croppedTop = part.CropTopTransparent(0);
parts.Add(new ImageSplitterItem(0, startY + croppedTop, part));
}
startY = key + 1;
}
lastKey = key;
}
if (bmp.Height - startY > minLineHeight && parts.Count > 0)
{
var part = bmp.CopyRectangle(new Rectangle(0, startY, bmp.Width, bmp.Height - startY));
if (!part.IsImageOnlyTransparent())
{
//part.GetBitmap().Save(@"j:\temp\split_" + parts.Count + ".bmp");
var croppedTop = part.CropTopTransparent(0);
parts.Add(new ImageSplitterItem(0, startY + croppedTop, part));
}
}
if (parts.Count == 0)
{
return new List<ImageSplitterItem> { item };
}
return parts;
}
private static bool CanGoUpAndRight(NikseBitmap bmp, int up, int right, int x, int y)
{
if (y - up < 0 || x + right >= bmp.Width)
{
return false;
}
for (int myY = y; myY > y - up && myY > 1; myY--)
{
var a = bmp.GetAlpha(x, myY);
if (a > 150)
{
return false;
}
}
for (int myX = x; x < x + right && myX < bmp.Width; myX++)
{
var a = bmp.GetAlpha(myX, y - up);
if (a > 150)
{
return false;
}
}
return true;
}
private static IEnumerable<ImageSplitterItem> SplitHorizontalNew(ImageSplitterItem lineSplitterItem, int xOrMorePixelsMakesSpace)
{
var bmp = new NikseBitmap(lineSplitterItem.NikseBitmap);