<<DiscreteWavelets`DiscreteWavelets`
Transform[Input_]:=Module[{Output,w,i},i=3;
(* This is the HWT that takes in a matrix and spits the blur out *)
w=80/(2^i);
Output=HWT2D[N[Input],NumIterations->i][[1;;w,1;;w]]]
Pad[L_]:=Module[{WF,HF,WL1,L1},WF:=ConstantArray[255,{Dimensions[L][[1]],80-Dimensions[L][[2]]}];
(* This function takes in a matrix and returns a standard 80x80 matrix, they all have to be the same size *)
HF:=ConstantArray[255,{80-Dimensions[L][[1]],80}];
WL1:=Join[L,WF,2];
L1:=ArrayFlatten[{{WL1},{HF}}];
Return[L1];]

GenerateCaptcha[]:=Module[{FullString,i,RotateAngle,RandomLocation,CAPTCHA,pgmout,Data},
(* This function generates the captcha and returns the matrix of it *)
FullString="ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
FullString="IVXCDLM0123456789";
(* We are using a reduced set of letters per the paper *)
RandomString="";
For[i=1,i<6,i++,RandomString=StringJoin[StringTake[FullString,{RandomInteger[{1,17}]}],RandomString]];
RotateAngle=RandomInteger[{-10,10}];
RandomLocation={RandomInteger[{100,500}],RandomInteger[{60,150}]};
CAPTCHA=Graphics[{Rotate[Inset[Text[Style[RandomString,FontSize->40,FontWeight->"Bold",Gray,FontFamily->"Helvetica"]],RandomLocation],RotateAngle Degree],Opacity[0],Rectangle[{0,0},{600,200}]},GridLines->{Table[i,{i,0,600,25}],Table[i,{i,0,200,25}]},GridLinesStyle->Directive[Black]];
Export["out.gif",CAPTCHA,"GIF"];
pgmout:=Import["out.gif"];
Export["out.pgm",pgmout,"PGM"];
Data=Import["out.pgm","Data"];
Data]


Unset[Cannon];
AllLetters="ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
AllLetters="IVXCDLM0123456789";
For[i=1,i≤StringLength[AllLetters],i++,Letter=StringTake[AllLetters,{i}];
(* For each letter generate a Canonical matrix for it *)
LetterArray[i]=Letter;
G1=Graphics[Text[Style[Letter,Gray,FontSize->40,FontWeight->"Bold",FontFamily->"Helvetica"],Background->White],AspectRatio->Automatic,ImageSize->{40,40}];
Export["Letter.pgm",G1,"PGM"];
ImageData=Import["Letter.pgm","Data"];
I1=Position[ImageData,127];
I1=I1.RotationMatrix[Pi/2];
Width=Ceiling[Max[I1[[All,1]]]-Min[I1[[All,1]]]];
Height=Ceiling[Max[I1[[All,2]]]-Min[I1[[All,2]]]];
G2=ListPlot[Round[I1],Axes->False,ImageSize->{2*Width,2*Height},AspectRatio->Automatic];
Export["G2.pgm",G2,"PGM"];
CroppedImageData=Import["G2.pgm","Data"];
Cannon[i]=Pad[CroppedImageData];
TransformedCannon[i]=Transform[Pad[CroppedImageData]];]
Compare[Input_]:=Module[{i,Result,NORM,NormSoFar},NormSoFar=10000;
(* This function takes in a matrix and compares it to all the letters, choosing the letter with the least norm *)
Result=1;
For[i=1,i≤StringLength[AllLetters],i=i+1,(*Compare 2-norms of Matrix*)NORM=N[Norm[(TransformedLetters[Input]-TransformedCannon[i]),2]];
If[(NORM<NormSoFar),NormSoFar=NORM;Result=i;];];
Result]
AlignCaptcha[Matrix_]:=Module[{Data,line,Rad,AlignedMatrix,Width,Height,CroppedImage},Data=Position[Matrix,127];
Data=Data.RotationMatrix[Pi/2];
line=Fit[Data,{1,x},x];
Rad=ArcTan[(line)[[2,1]]];
AlignedMatrix=Data.RotationMatrix[Rad];
Width=Ceiling[Max[AlignedMatrix[[All,1]]]-Min[AlignedMatrix[[All,1]]]];
Height=Ceiling[Max[AlignedMatrix[[All,2]]]-Min[AlignedMatrix[[All,2]]]];
g=ListPlot[AlignedMatrix,Axes->False,ImageSize->{2*Width,2*Height},AspectRatio->Automatic];
Export["out2.pgm",g,"PGM"];
CroppedImage=Import["out2.pgm","Data"];
CroppedImage]

ImageData=GenerateCaptcha[];
ImagePlot[ImageData]
CroppedImageData=AlignCaptcha[ImageData];
ImagePlot[CroppedImageData]

(*If you want to invert it,use the following line:CroppedImageData=Mod[CroppedImageData-255,255]^2;*)Dimensions[CroppedImageData][[2]];
Columns={};
For[i=1,i≤Dimensions[CroppedImageData][[2]],i=i+1,DataColumn=Mean[CroppedImageData[[All,i]]];
If[DataColumn==255,Columns=Append[Columns,i]]]
(*We need this extra column at the end,just in case there is no whitespace at the end*)
Columns=Append[Columns,Dimensions[CroppedImageData][[2]]];
Columns=Tally[Columns][[All,1]];

Unset[Letters]
NewIndex=1;
For[i=1,i≤Dimensions[Columns][[1]],i=i+1,BB=Columns[[i-1]];
CC=Columns[[i]];
If[BB+1≠CC,(*Print["From ",BB," to ",CC,"  (Value ",i,", NewIndex ",NewIndex,")"];*)Letters[NewIndex]=Pad[CroppedImageData[[All,BB;;CC]]];
NewIndex=NewIndex+1;]]

(*Loop this until we have gotten to the end of the image and all letters are cut*)
For[i=1,i≤5,i=i+1,Print[ImagePlot[Letters[i]]];]

For[i=1,i≤5,i=i+1,
TransformedLetters[i]=Transform[Letters[i]];
p1=ImagePlot[Letters[i],PlotLabel->"Original"];
B=HWT2D[N[Letters[i]],NumIterations->3];
p2=WaveletDensityPlot[B,NumIterations->3,PlotLabel->"HWT"];
Print[GraphicsGrid[{{p1,p2}},Frame->All]];
]

(*Join the letters together and return the decoded string*)
For[i=1,i≤5,i=i+1,Answers[i]=Compare[i];
Print[StringTake[RandomString,{i}]," = ",LetterArray[Answers[i]]];
]
TheAnswer="";
For[i=1,i≤5,i=i+1,Print[ImagePlot[Join[Letters[i],Cannon[Answers[i]],2]]];
TheAnswer=StringJoin[TheAnswer,LetterArray[Answers[i]]];]
Print["Decoded Answer = ",TheAnswer,"\n","Real answer    = ",RandomString];

(* Do[ *)
NumberOfTimes=100;
Counter=1;
Correct=0;
While[Counter≤ NumberOfTimes,
ImageData=GenerateCaptcha[];
CroppedImageData=AlignCaptcha[ImageData];
ImagePlot[CroppedImageData]
Columns={};
For[i=1,i≤Dimensions[CroppedImageData][[2]],i=i+1,DataColumn=Mean[CroppedImageData[[All,i]]];
If[DataColumn==255,Columns=Append[Columns,i]]];
Columns=Append[Columns,Dimensions[CroppedImageData][[2]]];
Columns=Tally[Columns][[All,1]];
Unset[Letters];
NewIndex=1;
For[i=1,i≤Dimensions[Columns][[1]],i=i+1,BB=Columns[[i-1]];
CC=Columns[[i]];
If[BB+1≠CC,Letters[NewIndex]=Pad[CroppedImageData[[All,BB;;CC]]];
NewIndex=NewIndex+1;]];
For[i=1,i≤5,i=i+1,TransformedLetters[i]=Transform[Letters[i]];];
For[i=1,i≤5,i=i+1,Answers[i]=Compare[i];
(* Print[StringTake[RandomString,{i}]," = ",LetterArray[Answers[i]]]; *)
]
Clear[TheAnswer];
TheAnswer="";
For[i=1,i≤5,i=i+1,
(* Print[ImagePlot[Join[Letters[i],Cannon[Answers[i]],2]]]; *)
TheAnswer=StringJoin[TheAnswer,LetterArray[Answers[i]]];];
(* Print["Decoded Answer = ",TheAnswer,"\n","Real answer    = ",RandomString]; *)
If[TheAnswer==RandomString,
Print[TheAnswer, " Decoded Correctly! ",Correct , " / ", Counter ]; Correct++;,Print["Couldn't Decode ",TheAnswer];
]
Counter++;
 ]
Print["We got ", Correct, " out of ", NumberOfTimes]