* Вначале на всем изображении выделяется область, ограничивающая отдельный символ.
* Затем эта область делится на 9 равных частей ( рис. 2 ).
рис. 2* В каждом из 9 квадратов подсчитывается число черных пикселов и делится на площадь данного квадрата, т.е. определяется плотность заполнения в каждом квадрате.
* Все 9 определенных плотностей преобразуются в формат Х.ХХХ и далее в строку типа Х.ХХХ Х.ХХХ Х.ХХХ Х.ХХХ Х.ХХХ Х.ХХХ Х.ХХХ Х.ХХХ Х.ХХХ.
Преобразование в строку производится для более удобного хранения данных в базе данных ( структура базы описана в приложении ), так как это намного удобнее, чем делать в базе 9 полей для хранения 9 значений плотности.
Декодирование символа производится аналогичным способом, только полученные данные сравниваются со значениями хранимыми в базе данных.
Все операции осуществляются посредством главного меню программы. Главное меню состоит из следующих пунктов:
1) Файл
· Открыть файл
Открывается окно выбора файла. Возможные маски для выбора ( BMP, PCX, JPG ).
Если выбранный файл является правильным графическим файлом, то хранимое в нем изображение выводится в окно программы.
· Выделить линии
..... Перед пользователем появляется диалоговое окно, в котором ему предоставляется возможность выбрать какие линии выделять ( вертикальные или горизонтальные ).
1) Операции
· Очистить
........... Тот файл, который был открыт открывается снова и все линии появившиеся в процессе работы удаляются.
· Определить плотность ( учеба )
Этот пункт меню предназначен для обучения системы. В окне должен находится эталонный текст. С помощью “мышки” выделяется нужный символ и выбирается данный пункт. Вслед за этим пользователю предоставляется возможность указать уникальный код для выбранного символа. Определенная плотность и код записываются в базу данных.
· Распознать
........... Этот пункт противоположен предыдущему. С помощью “мышки” выделяется нужный символ и выбирается данный пункт. Происходит определение плотности выбранного символа и далее в базе осуществляется поиск записи, у которой поле с эталонной строкой более сходно с плотностью выделенного символа.
........... Сравнение происходит следующим образом:
........... Определяется и складываются между собой разности между плотностями эталонного и выделенного символами для каждого квадрата. Тот эталонный символ, у которого полученная сумма окажется наименьшей считается эквивалентом для выделенного.
1) Преобразования
..... Эти преобразования являются экспериментальными и не являются целью поставленной задачи. Полученные результаты при более глубоком исследовании могут быть в дальнейшем использованы для обработки изображений.
· Афинное преобразование
· Узоры
........... Эти два пункта строят изображение пользуясь афинными преобразованиями с различными коэффициентами. В зависимости от этих коэффициентов изображения получаются различными.
· Лист папоротника
........... Опять же основываясь на афинных преобразованиях строится лист папоротника. Данное преобразование имеет огромное практическое значение, так как относительно сложное изображение ( лист папоротника ) строится с помощью одной формулы.
1) Выход
Выход из программы.
Приложение
Код символа | Эталонная строка |
Код символа - однозначно идентифицирует хранимый символ. Так как в базе хранятся эталоны иероглифов, для которых в русском алфавите нет примера начертания, то для замены распознанного символа нужно еще хранить и его эталонное изображение. Но так как целью данной работы является не замена распознанных символов на эталонные, а только соотнесение с эталоном, то для экономии дискового пространства решено хранить не эталонное изображение символа, а только его уникальный код, с помощью которого можно однозначно идентифицировать символ.
Эталонная строка - строка, содержащая в себе все 9 плотностей выделенной области.
{$I CdBase.inc}
{$I CdComp.inc}
unit Main;
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
Forms, Dialogs, Reg_imag, Menus, Options, CmplSign, DBTables, DB;
type
TMainForm = class(TForm)
MainMenu: TMainMenu;
N1: TMenuItem;
N2: TMenuItem;
Image: TMultiImage;
N3: TMenuItem;
NFileOpen: TMenuItem;
OpenDialog: TOpenDialog;
NSelect: TMenuItem;
N4: TMenuItem;
N5: TMenuItem;
N6: TMenuItem;
N7: TMenuItem;
Onemore1: TMenuItem;
N8: TMenuItem;
N9: TMenuItem;
DataTable: TTable;
N10: TMenuItem;
DataTableOpis: TStringField;
DataTableID: TFloatField;
procedure N2Click(Sender: TObject);
procedure NFileOpenClick(Sender: TObject);
procedure NSelectClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure ImageMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure ImageMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure ImageMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
procedure N4Click(Sender: TObject);
procedure N7Click(Sender: TObject);
procedure Onemore1Click(Sender: TObject);
procedure N8Click(Sender: TObject);
procedure N9Click(Sender: TObject);
procedure N5Click(Sender: TObject);
procedure N10Click(Sender: TObject);
private
DetectRectX, DetectRectY: real; { Угол, под которым выделять линии }
xStart, xEnd, yStart, yEnd: word;
BegSelect: boolean;
procedure DefGradient(var Gx, Gy: real; x,y: word);
procedure SetRect;
procedure DefPlotn;
procedure AfinConvert;
procedure OneMore;
procedure Mandel;
procedure Paporotnik;
function GetDensity: string;
public
{ Public declarations }
end;
var
MainForm: TMainForm;
implementation
{$R *.DFM}
procedure TMainForm.N2Click(Sender: TObject);
begin
Application.Terminate;
end;
procedure TMainForm.NFileOpenClick(Sender: TObject);
begin
if OpenDialog.Execute then begin
Image.ImageName := OpenDialog.FileName;
NSelect.Enabled := True;
end
else NSelect.Enabled := False;
end;
procedure TMainForm.NSelectClick(Sender: TObject);
var
Result: word;
begin
OptionForm := nil;
try
OptionForm := TOptionForm.Create(Self);
with OptionForm do begin
RectXEdit.Text := FloatToStr(DetectRectX);
RectYEdit.Text := FloatToStr(DetectRectY);
X1Edit.Text := IntToStr(xStart);
X2Edit.Text := IntToStr(xEnd);
Y1Edit.Text := IntToStr(yStart);
Y2Edit.Text := IntToStr(yEnd);
Result := ShowModal;
DetectRectX := StrToInt(RectXEdit.Text);
DetectRectY := StrToInt(RectYEdit.Text);
xStart := StrToInt(X1Edit.Text);
xEnd := StrToInt(X2Edit.Text);
yStart := StrToInt(Y1Edit.Text);
yEnd := StrToInt(Y2Edit.Text);
end; { with }
finally
OptionForm.Free;
end; { try }
if Result = mrOK then SetRect;
end;
{ Определение градиентов Gx и Gy в точке [x,y] }
procedure TMainForm.DefGradient(var Gx, Gy: real; x,y: word);
var
a, b, c, d, e, g, h, i: byte;
begin
with Image.Canvas do begin
if Pixels[x-1,y-1] = clBlack then a := 0
else a := 1;
if Pixels[x,y-1] = clBlack then b := 0
else b := 1;
if Pixels[x+1,y-1] = clBlack then c := 0
else c := 1;
if Pixels[x-1,y] = clBlack then d := 0
else d := 1;
if Pixels[x+1,y] = clBlack then e := 0
else e := 1;
if Pixels[x-1,y+1] = clBlack then g := 0
else g := 1;
if Pixels[x,y+1] = clBlack then h := 0
else h := 1;
if Pixels[x+1,y+1] = clBlack then i := 0
else i := 1;
{ Градиент по X }
Gx := g + 2*h + i - a - 2*b - c;
if Gx < 0 then Gx := 0;
if Gx = 0 then Gx := 0.000001;
{ Градиент по Y }
Gy := c + 2*e + i - a - 2*d - g;
if Gy < 0 then Gy := 0;
end; { with Image }
end;
procedure TMainForm.SetRect;
var
x, y: word;
Gx, Gy, Qx, Qy: real;
OutF: TextFile;
S1,S2: string;
begin
AssignFile(OutF, 'tangs.000');
Rewrite(OutF);
{ Сканируем все изображение }
with Image.Canvas do begin
for y := yStart+1 to yEnd-1 do begin
for x := xStart+1 to xEnd-1 do begin
DefGradient(Gx,Gy,x,y); { Определить градиент в точке [x,y] }
{if Gx+Gy > 0 then Pixels[x,y+200] := clRed;}
Qx := ArcTan(Gy/Gx);
Qx := Round(Qx*180/Pi);
{ Qx := Round(90*Gx/4);
Qy := Round(90*Gy/4);}
Str(Qx:2:0, S1);
{ Str(Qy:2:0, S2); }
Write(OutF, S1+{' '+S2+}' | ');
{ if (Q <= -Pi/3) or (Q >= Pi/3) then Pixels[x,y+200] := clRed;}
if (Qx > { DetectRectX}80) and (Qx < 100){ and (Q > DetectRect*Pi/180) }then
Pixels[x,y+200] := clRed;
end; { for x }
WriteLn(OutF, 'End Line');
end; { for y }
end; { with Image.Canvas }
CloseFile(OutF);
end;
procedure TMainForm.DefPlotn;
var
i, j, x, y, dx, dy, Range, x1, y1: word;
Count: word;
begin
x := xStart; y := yStart;
dx := Round((xEnd-xStart+1) div 3);
dy := Round((yEnd-yStart+1) div 3);
x1 := x; y1 := y;
{ Три квадрата по вертикали }
for i := 1 to 3 do begin
if i = 2 then Range := (yEnd-yStart+1) - 2*dy
else Range := dy;
{ Три квадрата по горизонтали }
for j := 1 to 3 do begin
if j = 2 then Range := (xEnd-xStart+1) - 2*dx
else Range := dx;
{ Сканируем внутри квадрата по y }
for y := y1 to y1+Range do begin
{ Сканируем внутри квадрата по x }
for x := x1 to x1+Range do begin
{ Подсчитываем число не белых пикселов }
if Image.Canvas.Pixels[x,y] <> clWhite then Inc(Count);
end; { for x }
end; { for y }
x1 := x1+dx; { Следующий квадрат по горизонтали }
end; { for j }
y1 := y1+dy; { Следующий квадрат по вертикали }
end; { for i }
end;
procedure TMainForm.FormCreate(Sender: TObject);
begin
OpenDialog.FileName := 'c:\delphi\mydir\diplom\pict\pict1.bmp';
Image.ImageName := OpenDialog.FileName;
end;
procedure TMainForm.ImageMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if Button = mbRight then begin
Image.ImageName := OpenDialog.FileName;
Exit;
end;
BegSelect := True;
with Image.Canvas do begin
Pen.Mode := pmXor;
Pen.Color := clGreen;
Pen.Style := psDot;
Brush.Style := bsClear;
xStart := X; yStart := Y;
xEnd := X; yEnd := Y;
end; { with }
end;
procedure TMainForm.ImageMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
BegSelect := False;
with Image.Canvas do begin
Pen.Mode := pmCopy;
Pen.Color := clBlack;
Pen.Style := psSolid;
Brush.Style := bsSolid;
end; { with }
end;
procedure TMainForm.ImageMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
begin
if not BegSelect then Exit;
with Image.Canvas do begin
Rectangle(xStart, yStart, xEnd, yEnd);
xEnd := X; yEnd := Y;
Rectangle(xStart, yStart, xEnd, yEnd);
end; { with }
end;
procedure TMainForm.N4Click(Sender: TObject);
begin
Image.ImageName := OpenDialog.FileName;
end;
{ Афинное преобразование }
procedure TMainForm.AfinConvert;
var
dx, dy, Rand: word;
A, B, C, D, E, F: real;
x, y: word;
i: longint;
begin
A := 0.5; B := 0.5; E := 0;
C := 0.3; D := 0; F := 1;
dx := (xEnd-xStart+1) div 2; xEnd := xStart +2*dx - 1;
dy := (yEnd-yStart+1) div 2; yEnd := yStart +2*dy - 1;
x := xStart+dx; y := yStart+dy;
Randomize;
for i := 1 to 50000 do begin
Rand := Random(10);
Case Rand of
0..3: begin
x := xStart + 1 + (x-xStart+1) div 2;
y := yStart + 1 + (y-yStart+1) div 2;
end;
4: begin
x := xStart + dx + (x-xStart+1) div 2;
y := yStart + 1 + (y-yStart+1) div 2;
end;
5: begin
x := xStart + 1 + (x-xStart+1) div 2;
y := yStart + dy + (y-yStart+1) div 2;
end;
6..9: begin
x := xStart + dx + (x-xStart+1) div 2;
y := yStart + dy + (y-yStart+1) div 2;
end;
end; { Case }
Image.Canvas.Pixels[x,y] := clBlue;
end; { for i }
end;
procedure TMainForm.N7Click(Sender: TObject);
begin
AfinConvert;