Nr58 Blaise 58 UK Total
Nr58 Blaise 58 UK Total
Nr58 Blaise 58 UK Total
R E L A T E D L A N G U A G E S / A N D R O I D,
I O S, M A C , W I N D O W S & L I N U X
P R I N T E D, P D F, & O N L I N E V I E W
CONTENTS
ARTICLES:
ADVERTISERS
BARNSTEN PAGE 4
BLAISE ONLINE VIEW INTRODUCTION PAGE 18
COMPONENTS4DEVELOPERS PAGE 40
NEXUS PAGE 39
Max Kleiner
www.softwareschule.ch
max @ kleiner.com
Wim Van Ingen Schenau -Editor Peter van der Sman Rik Smit
wisone @ xs4all.nl sman @ prisman.nl rik @ blaisepascal.eu
www.romplesoft.de
Editor - in - chief
Detlef D. Overbeek, Netherlands Tel.: +31 (0)30 890.66.44 / Mobile: +31 (0)6 21.23.62.68
News and Press Releases email only to [email protected]
Editors
Peter Bijlsma, W. (Wim) van Ingen Schenau, Rik Smit,
Correctors
Howard Page-Clark, James D. Duff
Trademarks
All trademarks used are acknowledged as the property of their respective owners.
Caveat Whilst we endeavour to ensure that what is published in the magazine is correct, we cannot accept responsibility for any errors or omissions.
If you notice something which may be incorrect, please contact the Editor and we will publish a correction where relevant.
Subscriptions ( 2013 prices )
1: Printed version: subscription € 80.-- Incl. VAT 6 % (including code, programs and printed magazine,
10 issues per year excluding postage).
2: Electronic - non printed subscription € 50.-- Incl. VAT 21% (including code, programs and download magazine)
Subscriptions can be taken out online at www.blaisepascal.eu or by written order, or by sending an email to [email protected]
Subscriptions can start at any date. All issues published in the calendar year of the subscription will be sent as well.
Subscriptions run 365 days. Subscriptions will not be prolonged without notice. Receipt of payment will be sent by email.
Subscriptions can be paid by sending the payment to:
ABN AMRO Bank Account no. 44 19 60 863 or by credit card: Paypal
Name: Pro Pascal Foundation-Foundation for Supporting the Pascal Programming Language (Stichting Ondersteuning Programeertaal Pascal)
IBAN: NL82 ABNA 0441960863 BIC ABNANL2A VAT no.: 81 42 54 147 (Stichting Programmeertaal Pascal)
Subscription department Edelstenenbaan 21 / 3402 XA IJsselstein, The Netherlands / Tel.: + 31 (0) 30 890.66.44 / Mobile: + 31 (0) 6 21.23.62.68
[email protected]
Copyright notice
All material published in Blaise Pascal is copyright © SOPP Stichting Ondersteuning Programeertaal Pascal unless otherwise noted and may
not be copied, distributed or republished without written permission. Authors agree that code associated with their articles will be made
available to subscribers after publication by placing it on the website of the PGG for download, and that articles and code will be placed on
distributable data storage media. Use of program listings by subscribers for research and study purposes is allowed, but not for commercial
purposes. Commercial use of program listings and code is prohibited without the written permission of the author.
end.
However, the uglyness is in the implementation:
Function TCarList.GetC (AIndex : Integer): TCar;
begin
Result:=TCar(Items[AIndex]); The getter and setter do not contain any code for
end;
checking the validity of the index, for simplicity.
It is clear from the code that the access to the
A typecast is necessary to cast the inherited Items elements in the array is direct, and that no
property (of type TObject) to TCar. It also typecasts are necessary because the elements are
requires a call to an inherited method. kept in a dynamic array of the correct type:
The setter is less problematic, as no typecast is FItems : Array of ET;
needed: To use this, one would write something like this:
Function TCarList.SetC (AIndex : Integer; AValue: TCar); Type
begin TCar = record
Items[AIndex]:=AValue; Brand : String;
end; Seats : Smallint;
end;
TCarList = TList<TCar>;
INDEXOF AND SORT: COMPARING ELEMENTS The following will then work:
Often, one needs to find the position of an element program cars5;
in the list (e.g. in order to remove it using delete). uses generics.defaults,list5;
Finding the position is usually handled with
IndexOf: Type
TCar = record
function IndexOf(const AValue: ET): Integer; Brand : String;
Seats : Smallint;
A naive implementation would be: end;
TCarList = TList<TCar>;
Function TList<ET>.IndexOf(Const AValue : ET) : Integer;
begin Var
Result:=Length(FItems)-1; MyCars : TList<TCar>;
While (Result>=0) and Not (AValue=FItems[Result]) do C1,C2 : TCar;
Dec(Result);
end; begin
MyCars:=TCarList.Create;
However, the compiler will complain about the try
C1.Brand:='BMW';
comparison, telling us that it doesn't know how to
C1.Seats:=2;
compare cars: MyCars.Add(C1);
list5.pas(30,36) C2.Brand:='Peugeot';
Error: Operator is not overloaded: "TCar" = "TCar" C2.Seats:=4;
MyCars.Add(C2);
In Free Pascal, the solution is to implement an MyCars.Delete(0);
operator overload of the "='' operatior for the TCar Writeln('Peugeot : ',MyCars.IndexOf(C2));
type. In Delphi, this is not sufficient. Instead, a Writeln('BMW : ',MyCars.IndexOf(C1));
comparer interface is needed. The comparer Finally
interface is defined in the Generics.Defaults unit MyCars.Free;
end;
(also supplied with Free Pascal): end.
IComparer<T> = interface
function Compare(constref Left, Right: T): Integer; overload;
end;
The code reports that no car brand Peugeot can be But this is cumbersome to call. Also, looking
found ! ahead it is reasonable to assume that we would
The reason is that the default comparer will add a Sort method to the list, and that will also
compare 2 records byte by byte. The first field of need a comparer interface. So, it is better to add
TCar (Brand) is a string, which in essence is a it as a field to the class, and pass it on in a
pointer to an array of characters. This means that constructor:
comparing the records byte by byte will compare Type
2 pointers. The UniqueString makes sure the TList<ET> = Class(TObject)
pointer to the characters Peugeot is unique, Private
FComparer : IComparer<ET>;
hence the default comparer is returning false, Public
even though the value of the string is the same. Constructor Create; overload;
To solve this properly, we need in essence to Constructor Create(AComparer : IComparer<ET>);
provide a proper comparer. This can be done overload;
end;
with the TComparer.Construct class function,
which we omitted from the earlier declaration:
Type
TComparisonFunc<T> = function(constref Left, Right: T): Integer;
Construct takes a function which compares 2 (other fields, methods and properties have been
variables of type T, and constructs an IComparer emitted for brevity). The implementation of the
interface for the type T. (the same can be done with a constructors is quite simple:
method instead of a plain function)
Thus, if we write a function Constructor TList<ET>.Create(AComparer :
IComparer<ET>);
Function CompareCars(constref C1,C2 : TCar) : integer; begin
begin FComparer:=AComparer;
if C1.Brand<C2.Brand then end;
Result:=-1
else if C1.Brand>C2.Brand then Constructor TList<ET>.Create;
Result:=1;
else begin
Result:=C1.Seats-C2.Seats; FComparer:=TComparer<ET>.Default;
end; end;
uses generics.defaults,list6;
Type
TCar = record
Brand : String;
Seats : Smallint;
end;
TCarList = TList<TCar>;
Var
MyCars : TList<TCar>;
C1,C2 : TCar;
CC : IComparer<TCar>;
begin
CC:=TComparer<TCar>.Construct(@CompareCars);
MyCars:=TCarList.Create(CC);
try
C1.Brand:='BMW';
C1.Seats:=2;
MyCars.Add(C1);
C2.Brand:='Peugeot';
C2.Seats:=4;
MyCars.Add(C2); Conclusion
MyCars.Delete(0); In this article, we discussed a use case for
Writeln('Peugeot : ',MyCars.IndexOf(C2)); generics which is quite common: creating a
Writeln('BMW : ',MyCars.IndexOf(C1)); container class. In implementing a sample
C1.Brand:='Peugeot'; container class, we encountered 2 common
C1.Seats:=4; problems; initializing a variable of unknown
UniqueString(C1.Brand); type, and comparing 2 variables of unknown
Writeln('Peugeot (2) : ',MyCars.IndexOf(C1)); type. The solution to the first problem led
Finally us to the use of the Default function, the
MyCars.Free; solution of the second problem led us to the
end; use of the IComparer interface and the
end. TComparer class. Luckily, one does not need
to implement such container classes
A nice side effect of using a comparer is that we manually: they are implemented in the
can construct two different lists, once with the generics.collections unit.
compare above, and for instance once with a
case insensitive comparer:
Function CompareCarsCI(constref C1,C2 : TCar) : integer;
begin
Result:=CompareText(C1.Brand,C2.Brand);
If Result=0 then
Result:=C1.Seats-C2.Seats;
end;
Issue Nr 10
9 2016
2016
BLAISE
BLAISE
PASCAL
PASCAL
MAGAZINE
MAGAZINE 9
9
SE
DIRK html
DAVID
mes.
HOWARD PAGE-CLARK
A SUDOKU HELPER/SOLVER PAGE 1/4
BY DAVID DIRKSE proce
var
dure
;
begin := 1 to
for i
begin
end ;
9 do
starter
end;
expert H&
R MAT
PUTE PASCAL
COM
ES IN
GAM
DATA STRUCTURES
This article describes a Delphi project to assist in the type TMove = (mtFree,mtOrg,mtPlay);
solving of sudoku puzzels.
This assistance includes : TField = record
● entry, saving and opening of puzzles mt : TMove;
● display of options move : byte;
● finding solutions by adding/removing numbers options : word;
or options end;
● full solution by computer and test for uniquness.
● game analyses: show possible option reductions var game : array[1..9,1..9] of Tfield;
In above picture:
kolom = column;
rij = row;
veld = field;
groep = group Below is a picture of the program at work.
Note:
A Sudoku puzzle is a square of 9*9 fields. nieuw = new;
tonen = show;
These fields may be regarded as 9 rows,
berekenen = calculate;
9 columns or 9 groups of 9 fields each. oplossen = solve;
opties = options
A solved puzzle has numbers 1..9 in
each field of a row, column or group.
So a number appears just once in each
row, column or group.
A puzzle comes with 17 to about
30 numbers already filled in.
The puzzler has to fill in the remaining
open fields using logic reasoning.
Sudoku puzzles come in great variety
of difficulty.
The ones in daily papers are easy 2(**)
to hard 4(****).
DAVID
mes.
HOWARD PAGE-CLARK
A SUDOKU HELPER/SOLVER PAGE 2/4 proce
var
dure
;
begin := 1 to
for i
begin
9 do
end ;
end;
H&
R MAT
PUTE PASCAL
COM
ES IN
The brown numbers are part of the original This approach allows for solving 2(**) GAM
puzzle. (type mtOrg ). The checkbox (right to 4(****) puzzles. The last method is needed
column) is checked to display the options in more frequently in case of the 4(****) puzzles.
empty fields. The above puzzle is very difficult, Now follows an interesting conclusion:
9 stars. The button below the checkbox allows above methods are both simple examples of one
for recalculation of all options. general rule:
This is how recalculation works:
procedure recalcOptions; If K fields (of a row, column or group) together
//recalculate all option fields have exactly K opitons, these options cannot
var rowopts : array[1..9] of word; //word per row occur in the other (9-K) fields.
colopts : array[1..9] of word; //word per column
grpopts : array[1..9] of word; //word per group We solve puzzles by reducing options.
cc,rr,i,grp : byte; //cc:column; rr:row; grp:group
If each field has only one option left,
mask : word; //shifting "0"
begin the puzzle is solved.
for i := 1 to 9 do
begin
colopts[i] := $3fe; //all options EXAMPLES:
rowopts[i] := $3fe; K=1
grpopts[i] := $3fe;
end;
for i := 1 to 81 do //per field
begin
grp := field2Group(i); //group of field i
field2CR(cc,rr,i); //column, row of field i
with game[cc,rr] do Row 1, column 4 has single option 7 so this
if move <> 0 then option may be removed from the other columns.
begin
mask := (1 shl move) xor $3fe; K=8
colopts[cc] := colopts[cc] and mask;
rowopts[rr] := rowopts[rr] and mask;
grpopts[grp] := grpopts[grp] and mask;
end;
end;
for i := 1 to 81 do
begin Column 2 is the only field with option 4 in
grp := field2Group(i); row 4. The other options of this field must be
field2CR(cc,rr,i); removed.
with game[cc,rr] do
In other words: columns 1,3,4,5,6,7,8,9 have
if move = 0
then options := colopts[cc] and rowopts[rr] all options other than 4, so all options other
and grpopts[grp] than 4 outside these fields may be deleted.
else options := 1 shl move; Note: a number in a field counts as a single
end; option.
end;
K=2
DAVID
mes.
HOWARD PAGE-CLARK
A SUDOKU HELPER/SOLVER PAGE 3/4 proce
var
dure
;
begin := 1 to
for i
begin
9 do
end ;
end;
H&
R MAT
PUTE PASCAL
COM
ES IN
The 7 options 1,3,4,5,6,7,9 occupy exactly 7 GAM
DAVID
mes.
HOWARD PAGE-CLARK
A SUDOKU HELPER/SOLVER PAGE 3/4 proce
var
dure
;
begin := 1 to
for i
begin
9 do
end ;
end;
H&
R MAT
PUTE PASCAL
COM
ES IN
Not all puzzles can be solved with these tricks. Boolean flags remember options being GAM
In some cases the interaction between rows, scrapped. Again: these procedures are too long
columns and groups is more complicated. to be printed here. Please see the source code.
I have added following approach: if methods
before fail to reduce options, an option is simply All reduction methods are called by procedure
filled in and the K=1,2... reduction is applied. AnalyseGame.
If the number inserted was wrong, these This procedure switches back to the most simple
reductions may remove all options from a field reduction method after options were scrapped.
somewhere making the puzzle unsolvable. We keep things as simple as possible.
So, this filled in option must be removed. The project also contains a function
For these tests the puzzle options are copied to a SolveBruteForce which solves every
special array[1..9,1..9] of word : Zgame. Sudoku puzzle however difficult. Numbers are
(Z of zero option search). systematically filled in until the solution is
Reason is not to modify the game option fields. encountered. The disadvantage of this method is
obvious: it does not add to our understanding or
Below is an example. logic reasoning at all.
Field 2 of row 1 has option 1 filled in. Following The SOLVE (oplossen) button may be pressed
option reduction resulted in zero options for the repeatedly to search for additional solutions.
field at column 9, row 2. If more then one solution is found, the puzzle is
Option “1” in column 2, row 1 must be deleted. wrong. Good Sudoku puzzles have one unique
solution.
Postscript
This Sudoku helper/solver has some
extras:
1. display of options facilitate the
solution of difficult puzzels
2. options may be removed by a right
mouseclick
3. analysis shows logic reasoning of
This work is done by function searchZfield : step by step option reduction
boolean which has several helpers:
loadZOptions :
copies options to Zgame[ , ] array
dropZOption : deletes options in row,
column and group of a field after filling in a
number
checkZrows : test rows, calls reduceXZ for
K=1,2 reductions
checkZcolumns : same for columns
checkZgroups : same for rows
Let's run our sample code - but at this time we are I'd like to create collators once in the module
going to create TfrxStringList, so we need to initialization:
change one line:
List := TfrxStringList.Create; var
collatorRef: Pointer;
Same machines results: collatorRefIgnoreCase: Pointer;
Windows 7 – 2ms.
Mac OS – 3 ms!
initialization
UCCreateCollator(nil, 0,
QUITE EASY? kUCCollateCaseInsensitiveMask,
And we can use the old code with the associative @collatorRefIgnoreCase); // ignore case sensetive
TstringList. This trick will work for most UCCreateCollator(nil, 0, 0, @collatorRef); // default
cases when TstringList was used as finalization
associative array(name cache, parameters and so on). UCDisposeCollator(@collatorRefIgnoreCase);
But can we speed up Unicode comparison for Mac UCDisposeCollator(@collatorRef);
OS? There is another function in he Mac API which
works directly with null terminated strings and it
works much faster.
try
UCCompareText(collatorRef, PWideChar(s1), Length(s1), PWideChar(s2), Length(s2), @Res, @Result);
if Res then
Result := 0;
finally
end;
end;
{$ELSE}
begin
Result := CompareStr(s1, s2);
end;
try
UCCompareText(collatorRefIgnoreCase, PWideChar(s1), Length(s1), PWideChar(s2), Length(s2), @Res,
@Result);
if Res then
Result := 0;
finally
end;
end;
{$ELSE}
begin
Result := CompareText(s1, s2);
end;
{$ENDIF}
The same Mac OS machine presents now 61 ms There is another way. We can cache used fonts in
(150ms with original FMX code) with the same lists and select them when draw something.
example code and Unicode support in comparison For GDI plus canvas it's not hard to do and we
(call to Mac API). Not so bad at all! can use some hacks.
Too bad that FireMonkey framework uses such type
slow string functions even in the core functionality TTextLayoutHack = class(TTextLayout);
like serialization and unfortunately we can't change // create layout once (or you can make a list
that. But at least we can speed up our code a bit. // of layouts with different settings)
Layont := TTextLayoutManager
SUCH A LOT OF WORDS. .TextLayoutByCanvas(Canvas.ClassType)
Another important thing in my opinion is the .Create(Canvas);
text output. If we look at the canvas Layont.BeginUpdate;
Layont.Font := Canvas.Font;
implementation we will see that FillText is
Layont.WordWrap := WordWrap;
deprecated and we need to use TTextLayout. Layont.HorizontalAlign := TextAlign;
Let us consider the situation when we need to Layont.VerticalAlign := VTextAlign;
output a lot of text in different places (from 20 – Layont.RightToLeft := [];
100 text outputs). What does FireMonkey Layont.EndUpdate;
Layont.BeginUpdate; // that's need to force
framework suggest?
// layout from front update
Create TTextLayout set parameters and do the // now we can draw text as many as we want
same for another text output, but here is a // and where we need.
hidden pitfall. When we change a text or other // Draw cycle start
parameter of TextLayout it will force Layout to Layont.Color := Canvas.Fill.Color;
recreate everything include Font. Font creation Layont.Opacity := Opacity;
Layont.TopLeft := Arect.TopLeft; // position of the text
is one of the most performance consuming
Layont.MaxSize := PointF(ARect.Width, Arect.Height);
operation for both Windows and Mac OS. If you // size of the text box
develop something for VCL you may still Layont.Text := Atext; // new text to output
remember how we usually draw some text on TTextLayoutHack(Layont).DoDrawLayout(Canvas);
canvas it still is simple:
Canvas.Font := AssignFontProperties;
Canvas.TextOut();
Canvas.TextOut(); Unfortunately it works only for GDI Plus canvas ,
Canvas.TextOut(); for Mac OS we have to write all the text draw.
Canvas.TextOut(); Thats what we've done with FastReport FMX , the
code is way too large to show it here and partially
We were able to create Font only once! Now it's duplicates Framework output but with fonts
not possible, because all implementations cached in the list. That allows significant increase
hidden inside the exact platform classes. text output for Mac OS.
In the FastReport-Designer we have a simple You can check everything written here with the
Ruler-control and even with this control we new version of FastReport FMX and of course look
have a performance hit. Here how it looks like. at the source codes.
Now just imagine two Ruler-controls in the report These are not all the problems we can face using
designer can make the Report Designer almost the FireMonkey framework , but it is a good start to
unsuitable for Mac OS. understand its weak points and design your
The Framework has only two options: application more carefully.
3 4
1 2 5
6 10 12
7 8 9 11
13 18 19
14 15 16 17 27
20 25 26
21
28 22 23 24 35 36 37
29 30 31+ 33 34
32
32 40 41 42
38 39 45+ 43
46 47 48 49
44
50 51 53 56
58
52
54+ 57
55
N
S OO
NG
O MI SOON
C NG
MI ON
CO NG SO
I N
COMG SOO
IN
COM SOON
ING
COM SOON
WINDOWS 10
ING
COM SOON
NG
COMI
OON
COMING S
INTRODUCES THE NEW
O N L I N E V I E W
L I B R A R Y 2 0 1 7
http://www.blaisepascal.eu/OnlineView/Overview/OnlineView_Overview.html
DAVID
mes.
HOWARD PAGE-CLARK
BY DAVID DIRKSE proce
var
dure
;
begin := 1 to
for i
9 do
begin
end ;
starter
end;
expert H&
R MAT
PUTE PASCAL
COM
ES IN
Delphi 7 including BERLIN GAM
DAVID
mes.
HOWARD PAGE-CLARK
COMBINATIONS PAGE 1/2 proce
var
dure
;
begin := 1 to
for i
begin
9 do
end ;
end;
H&
R MAT
PUTE PASCAL
COM
ES IN
function advanceSH : boolean; Using and we can select bits, xor GAM
• SH : the shifter
• mask: only 1 bit set tot select an element.
(mask = 1 shl pos)
• Pos: the position of the mask bit.
• Count: the number of removed “1” bits
in SH, to be inserted later
A B A and B A xor B
0 0 0 0
0 1 0 1
1 0 0 1
1 1 1 0
In the Load Picture dialog , select image containing and drop it on the Form. We will need to expand
the mask, then click on the “Open” button: the size of the generated mask to allow for it to
always fit after the rotation and to match the size
for the rotated training image.
In the Object Inspector expand the Size property.
Set the value of the Mode sub-property of the Size
property to asCustom. Set the values of the Height
and Width sub-properties of the Size property to
46: (Next page top left)
Switch back to the Form Editor by clicking on the We will also add a Label to display the progress
“Design” tab. Finally we need to add the of the training: Type “label” in the Tool Palette
classifier. Type “radial” in the Tool Palette search box, then select TLabel from the palette:
search box, then select
TILRadialBasisFunctionNetwork from the
palette:
VLGenericFilter1.SendStartCommand(
Display the training progress:
32, 32, 1000 );
Label1.Caption :=
for J := 1 to 9 do
J.ToString() + ' ' +
begin
FCategory.ToString();
for I := 0 to 40 do
begin
FCategory := I; If the training is for the
Label1.Caption := J.ToString() + ' ' background we will set the
+ FCategory.ToString(); AImageBuffer to be black,
if( I = 0 ) then and the Category to -1:
begin
AImageBuffer.SetGrayScale( 0 );
FCategory := -1;
end
else
begin
ABitmap.LoadFromFile(
'C:\Program Files (x86)\
Embarcadero\Studio\18.0\
LabPacks\Demos\
AVIFiles\Faces\Train_' + I.ToString() + '_'
+ J.ToString() + '.bmp' );
AImageBuffer.FromBitmap( ABitmap );
end;
AImageBuffer.Format := vfGrayScale;
for AAngle := 0 to 36 do
begin
VLRotate1.Angle := AAngle * 10;
VLRotate2.Angle := AAngle * 10;
VLImageGen1.Pump();
VLGenericFilter1.SendData( AImageBuffer );
Application.ProcessMessages();
end;
end;
end;
ABitmap.Free();
VLGenericFilter1.SendStopCommand();
ILRadialBasisFunctionNetwork1.SaveToFile( 'C:\Files\
MaskedRotatedFaceTraining14x14_BKG.ctd' );
end;
begin
InBuffer.Format := vfGrayScale;
AFeaturesBuffer := TSLBlockBuffer.CreateData( InBuffer.Read(),
InBuffer.ByteSize );
AMaskBuffer := TSLBlockBuffer.CreateData( FMaskImage.Read(),
FMaskImage.ByteSize );
ILRadialBasisFunctionNetwork1.Train( AFeaturesBuffer,
AMaskBuffer, FCategory );
end;
Here we set the Format of the InBuffer to At this point you will have the training saved in
vfGrayScale, then copy the data from InBuffer the “MaskedRotatedFaceTraining14x14_BKG.ctd” file
and FMaskImage to ISLBlockBuffer buffers: in the “C:\Files” directory.
AFeaturesBuffer := TSLBlockBuffer.CreateData( Here is the complete code for the training project:
InBuffer.Read(), InBuffer.ByteSize ); unit Unit1;
AMaskBuffer := TSLBlockBuffer.CreateData( interface
FMaskImage.Read(), FMaskImage.ByteSize );
uses
Winapi.Windows,Winapi.Messages, System.SysUtils,
and train the classifier with the 2 buffers and the System.Variants, System.Classes, Vcl.Graphics,
Fcategory: ILRadialBasisFunctionNetwork1.Train( Vcl.Controls, Vcl.Forms, Vcl.Dialogs, VLResize,
AFeaturesBuffer, AMaskBuffer, FCategory ); Mitov.VCLTypes, VCL.LPControl,
SLControlCollection, VLCommonDisplay,
VLImageDisplay, VLCommonFilter,
In our previous applications we use Real Buffers to VLRotate, Mitov.Types, LPComponent,
train and recognize. The IntelligenceLab classifier SLCommonFilter, VLBasicGenericFilter,
can also work with Block Buffers containing bytes, VLGenericFilter, Vcl.StdCtrls, ILBasicClassifier,
and automatically will normalize the buffers. ILRadialBasisFunctionNetwork, SLCommonGen,
The training in this case is smaller, and the
VLCommonGen, VLImageGen;
processing is faster.
type
TForm1 = class(TForm)
VLGenericFilter1: TVLGenericFilter;
The project is ready and we can run it. VLRotate1: TVLRotate;
Before you run it, make sure to create the VLImageDisplay1: TVLImageDisplay;
“C:\Files” directory where the training will be VLResize1: TVLResize;
saved. VLGenericFilter2: TVLGenericFilter;
VLImageGen1: TVLImageGen;
VLRotate2: TVLRotate;
If you click on the Train button of the running VLResize2: TVLResize;
project, you will see the rotated training images VLGenericFilter3: TVLGenericFilter;
displayed, and the Label will show you the indexes ILRadialBasisFunctionNetwork1:
TILRadialBasisFunctionNetwork;
of the bitmap file that is being processed:
Button1: TButton;
Label1: TLabel;
procedure Button1Click(Sender: TObject);
procedure VLGenericFilter2ProcessData(
Sender: Tobject; InBuffer: IVLImageBuffer;
var OutBuffer: IVLImageBuffer;
var SendOutputData: Boolean);
procedure VLGenericFilter3ProcessData(
Sender: Tobject; InBuffer: IVLImageBuffer;
var OutBuffer: IVLImageBuffer;
var SendOutputData: Boolean);
private
FCategory : Integer;
FMaskImage : IVLImageBuffer;
public
{ Public declarations }
end;
var
Form1: Tform1; see further next page
implementation
{$R *.dfm}
end.
And drop
it on the form.
32
ARTIFICIAL INTELLIGENCE PART III: PAGE 13 / 18
FACIAL RECOGNITION IMPROVED
AGraphics := NIL;
OutBuffer.FromBitmap( ABuffer );
ABuffer.Free();
end;
Select the OnStart event, and double click on the
value edit to generate event handler:
procedure TForm1.VLGenericFilter1Start(Sender: TObject; var AWidth,
AHeight: Integer; AFrameDelay: Real);
begin
VLGenericFilter3.SendStartCommand( AWidth, AHeight, Round( AFrameDelay ));
end;
Here we will simply start the VLGenericFilter3 First we will send the arriving InBuffer to
with the same parameters we get in the event. VLGenericFilter2 component by calling
Switch to the Form Editor by clicking on the SendData:
“Design” tab.
Select the OnProcessData event, and double
VLGenericFilter2.SendData( InBuffer );
click on the value edit to generate event handler:
To paint on the buffer we will convert it to
temporary bitmap:
ABuffer := TBitmap.Create();
InBuffer.ToBitmap( ABuffer );
And for each detection will draw rectangle and The buffers that arrive from the VLSampler1
show detection information: component connected to the
AGraphics.DrawRectangle( TIGPPen.Create( aclRed ), VLGenericFilter3 contain additional
ADetection.Position.X - 23, ADetection.Position.Y - information about the location where they are
23, 46, 46 );
AGraphics.DrawString( Detection.Index.ToString(),
extracted from. We will access this information
AFont, ADetection.Position, and store it in the FDetectedPosition field:
TIGPSolidBrush.Create( aclRed )); ALocation := InBuffer.GetCustom( 0 ) as
IVLSamplerLocationData;
Finally we will release the AGraphics, convert FDetectedPosition := ALocation.Position;
the bitmap to the output buffer, and release the
bitmap: Next we will assign the pixels from the image to
AGraphics := NIL; an ISLBlockBuffer and will send the buffer
to ILRadialBasisFunctionNetwork1 for
OutBuffer.FromBitmap( ABuffer ); recognition:
ABuffer.Free();
AFeaturesBuffer := TSLBlockBuffer.CreateData(
To use the IGDI+, we will need to add the InBuffer.Read(), InBuffer.ByteSize );
following units to the uses clause: LRadialBasisFunctionNetwork1.Predict(
AFeaturesBuffer );
uses IGDIPlus, VCL.IGDIPlusExt;
Next we need to write code to process and send Finally we will call the Predict method of the
for recognition each extracted sample. ILRadialBasisFunctionNetwork1 to try to
Switch to the Form Editor by clicking on the recognize the image.
“Design” tab. The only remaining task is to get the results of
Select the VLGenericFilter3 component. the recognition from the classifier.
Select the OnProcessData event, and double click Switch to the Form Editor by clicking on the
on the value edit to generate event handler: “Design” tab.
Select the ILRadialBasisFunctionNetwork1
component.
Double click on the value of the OnResult
event to generate event handler:
begin
ALocation := InBuffer.GetCustom( 0 ) as IVLSamplerLocationData;
FDetectedPosition := ALocation.Position;
InBuffer.Format := vfGrayScale;
public
{ Public declarations }
end;
var
Form1: TForm1;
In this article I showed you how to improve the recognition of rotated faces in a large image, by
using rotated training with weighted(masked) samples. We also reduced the false recognitions by
training for the background with black image. In all the examples until now we have used only the
image pixels as features. They are good features to use, but if we use higher level features, such as
already detected shapes, or regions in the image, we can improve the recognition. The processing
is also still relatively slow. In the following articles, I will show you how you can improve the
recognition results by using higher level features, and how you can improve the performance by
executing the recognition in parallel over multiple cores or in a GP GPU.
- Native high performance 100% developer defined app kbmMemTable is the fastest and most feature rich in
server with support for loadbalancing and failover memory table for Embarcadero products.
- Native high performance JSON and XML (DOM and - Easily supports large datasets with millions of records
SAX) for easy integration with external systems - Easy data streaming support
- Optional to use native SQL engine
- Native support for RTTI assisted object marshalling to
- Supports nested transactions and undo
and from XML/JSON, now also with new fullfeatured XML - Native and fast build in M/D, aggregation /grouping,
schema (XSD) import range selection features
- High speed, unified database access (35+ supported - Advanced indexing features for extreme performance
database APIs) with connection pooling, metadata and
Whats new in v. 4.94.00
data caching on all tiers
- New improved XSD converter.
- Multi head access to the application server, via AJAX, - New automatic password generator algorithms
native binary, Publish/Subscribe, SOAP, XML, RTMP from (currently Mixer and Koremutake supported)
web browsers, embedded devices, linked application - Object marshalling support with custom enum
servers, PCs, mobile devices, Java systems and many definitions
more clients - New Logformatter framework for custom log formats
- Full FastCGI hosting support. Host PHP/Ruby/Perl/ - New REST CORS support
Python applications in kbmMW! - New random generator framework currently
supporting SplitMix (64 bit), Xoroshiro128+/
- AMQP support ( Advanced Message Queuing
1024 (64 bit), PCG (32 bit), Mersenne Twister
Protocol) (32 bit/64 bit) random generators.
- Added AMQP 0.91 client side gateway support and - Further improved scheduler framework, now also
sample. with runonce async functions.
- Fully end 2 end secure brandable Remote Desktop with - Much improved HTTP/REST header support
near REALTIME HD video, 8 monitor support, texture - Support for automatic log file backup and many
other log improvements
detection, compression and clipboard sharing.
- Support for async request with anonymous function
- Supports Delphi/C++ Builder/RAD Studio 2009 to 10.1 callback (and more) in WIB.
Berlin (32 bit/64 bit, Android, IOS 32/64 and OSX where - Improved advanced dataset resolver features
applicable)" - Improved XML SAX/DOM parser/generator
COMPONENTS
DEVELOPERS 4
EESB, SOA,MoM, EAI TOOLS FOR INTELLIGENT SOLUTIONS. kbmMW IS THE PREMIERE N-TIER PRODUCT FOR DELPHI /
C++BUILDER BDS DEVELOPMENT FRAMEWORK FOR WIN 32 / 64, .NET AND LINUX WITH CLIENTS RESIDING ON WIN32 / 64, .NET, LINUX, UNIX
MAINFRAMES, MINIS, EMBEDDED DEVICES, SMART PHONES AND TABLETS.