-- ( ) --Bhautik Joshi, 2210847 [..]------| --comp1011 (computing 1A) assignment. MOO?--->[ ] | --Worlds most pesky OCR program, coded in Haskell. [oo]------| -- -- |||| /\ --import list functions import List -- --Chop Functions --~~~~~~~~~~~~~~ --Chops off excess at the top chop_off_top:: [[Char]] -> [[Char]] chop_off_top l |l==[] =error "No character!(chop_off_top)" |sumbitsrow (head l)==0 =chop_off_top (tail l) |otherwise =l --Is the firstrow a zero row? firstrowzero:: [[Char]] -> Bool firstrowzero l |l==[] =error "No input you doof!" |sumbitsrow (head l) == 0 =True |otherwise =False --Sum all of them bits in a row sumbitsrow:: [Char] -> Int sumbitsrow l |l==[] =0 |head l=='0' =0 + sumbitsrow (tail l) |otherwise =1 + sumbitsrow (tail l) --Chop off top and bottom of the character chop2::[[Char]] -> [[Char]] chop2 l = reverse (chop_off_top (reverse (chop_off_top l))) --The final wrapper function for trimming the character: trim::[[Char]] -> [[Char]] trim l |l==[] =error "No character!(trim)" |otherwise =transpose (chop2 (transpose (chop2 l))) -- --Ratio routines --~~~~~~~~~~~~~~ --Height of a character height:: [[Char]] -> Int height l = length l --Width of a character width:: [[Char]] -> Int width l = length (head l) --Aspect ratio (height divided by width) ratio_f:: Int -> Int -> Float ratio_f h w |w==0 =error "Divide by zero!(ratio_f)" |otherwise =(fromInt h) / (fromInt w) --Final ratio function ratio:: [[Char]] -> Float ratio l = ratio_f (height l) (width l) -- --Bit pattern operations --~~~~~~~~~~~~~~~~~~~~~~ --These bundles of fun attack characters and return 'simplified' versions of the characters --Add a thin stuffed crust of zeroes around the edge of the trimmed character stuffedcrust:: [[Char]] -> [[Char]] stuffedcrust l = transpose (stuffedcrust_a (transpose (stuffedcrust_a l))) --subfunction to add a zero to either side of a row stuffedcrust_a:: [[Char]] -> [[Char]] stuffedcrust_a l |l==[] =error "" |otherwise =(e: l) ++ [e] where e=(zsforcrust (head l)) --Convert a string of anything into an equivalent sized crusty row of zeroes --eg "10100011" -> "00000000" zsforcrust:: [Char]->[Char] zsforcrust l |l==[] =[] |otherwise ='0':zsforcrust (tail l) --Gets the bit pattern for a particular row --This is basically shows how many times a line through the row cuts a line. --eg for the line "000111000111000111" would return "0101010" --This is a neat trick for getting around serifs when combined with stuffedcrust bitp:: [[Char]] -> Int -> [Char] bitp l n |l==[] =error "eh?" |otherwise =map head (group(l!!n)) --Finds middle column and performs a bit pattern operation on it middlepat:: [[Char]] -> [Char] middlepat l |l==[] =error "What crap are you feeding me?" |otherwise =bitp (transpose l) n where n =((length (head l)) `div` 2) --gets the bit pattern for the row 1/4 way down fstrowpat4:: [[Char]] -> [Char] fstrowpat4 l |l==[] =error "" |otherwise =bitp l n where n=floor ((fromInt (m))/4) m=(length (head (transpose l))) --gets the bit pattern for the row 1/4 way up lstrowpat4:: [[Char]] -> [Char] lstrowpat4 l |l==[] =error "" |otherwise =bitp (reverse l) 1 --gets a general result for any particular character finalpat4:: [[Char]] -> [[Char]] finalpat4 l |l==[] =error "" |otherwise =[fstrowpat4 k]++[lstrowpat4 k]++ [middlepat k] where k=stuffedcrust l --gets the bit pattern for the row 1/3 way down fstrowpat3:: [[Char]] -> [Char] fstrowpat3 l |l==[] =error "" |otherwise =bitp l n where n=floor ((fromInt (m))/3) m=(length (head (transpose l))) --gets the bit pattern for the row 1/3 way up lstrowpat3:: [[Char]] -> [Char] lstrowpat3 l |l==[] =error "" |otherwise =bitp l n where n=floor (2*((fromInt (m))/3)) m=(length (head (transpose l))) llrow:: [[Char]] -> [Char] llrow l = map head (group((reverse l)!!0)) --gets a general result for any particular character finalpat3:: [[Char]] -> [[Char]] finalpat3 l |l==[] =error "" |otherwise =[fstrowpat3 k]++[lstrowpat3 k]++ [middlepat k] where k=stuffedcrust l -- --Line operations --~~~~~~~~~~~~~~~ --Mostly for b, d and k, to detect a vertical line on the left or right hand side of the character. --Firstly produce a tuple of the lengths of the sequences ones in vertical rows: count1s:: [[Char]] -> [[Int]] count1s l |l == [] =[] |otherwise =(lengthofones l 0): (count1s (transpose (tail (transpose l)))) --Length of ones n columns across (first row, n==0) lengthofones:: [[Char]] -> Int -> [Int] lengthofones l n |l==[] =error "" |otherwise =lengthofones_a (group ((transpose l)!!n)) lengthofones_a:: [[Char]] -> [Int] lengthofones_a l |l==[] =[] |head(head l)=='1' =length (head l): lengthofones_a (tail l) |otherwise =0: lengthofones_a (tail l) --Get that tuple, convert it to a plain list of ints. by taking the maximum value of the --lengths of a column of ones in any particular column. max1s:: [[Char]] -> [Int] max1s l = map maximum (count1s l) --Okay, you have now a list of ints, giving the maximum length of any column of ones for each --column, going from left to right. Now, the trick is to find out WHERE the density of ones are --greatest - the left or the right. Serifs suck, because they stuff up the calculations. We can --try, anyway, so, the function chops the list in two, and sees which has a greater average --density of 1s. --Test with- zip (map lorrline (map trim ti)) til --Left or right is the greatest length line function lorrline:: [[Char]] -> Char lorrline l = lorrline_a (max1s l) lorrline_a:: [Int] -> Char lorrline_a l |l==[] =error "" |lftn <= 0 ='R' |rhtn <= 0 ='L' |(lftsum/lftnf) > (rhtsum/rhtnf) ='L' |otherwise ='R' where lftn = ceiling ((fromInt (length l))/2) rhtn = (length l) - lftn lftnf = fromInt lftn rhtnf = fromInt rhtn lftlst = take lftn l rhtlst = drop lftn l lftsum = (fromInt (sum lftlst)) rhtsum = (fromInt (sum rhtlst)) -- --Density Tests --~~~~~~~~~~~~~ --Functions to identify blobs on characters, identifying top- or bottom-heavy characters. --Take some of the functions from line ops. (above) and modify them for horizontal use count1sh:: [[Char]] -> [[Int]] count1sh l |l == [] =[] |otherwise =(lengthofonesh l 0): (count1sh (tail l)) lengthofonesh:: [[Char]] -> Int -> [Int] lengthofonesh l n |l==[] =error "" |otherwise =lengthofones_a (group (l!!n)) --sum the number of ones in all the rows; this is where this set of functions differ from the --line operations above; rather than find the longest line, find the greatest density of zeroes. sum1s:: [[Char]] -> [Int] sum1s l = map sum (count1sh l) --Nick more of the line ops- hey, they work, so if it 'aint broke, dont fix it! --example test use- zip (map torbweight (map trim ti)) til --Top or bottom weight function. torbweight:: [[Char]] -> Char torbweight l = torbweight_a (sum1s l) torbweight_a:: [Int] -> Char torbweight_a l |l==[] =error "" |lftn == 0 ='B' |rhtn == 0 ='T' |(lftsum/lftnf) > (rhtsum/rhtnf) ='T' |otherwise ='B' where lftn = ceiling ((fromInt (length l))/2) rhtn = (length l) - lftn lftnf = fromInt lftn rhtnf = fromInt rhtn lftlst = take lftn l rhtlst = drop lftn l lftsum = (fromInt (sum lftlst)) rhtsum = (fromInt (sum rhtlst)) -- --Reasoning routines --~~~~~~~~~~~~~~~~~~ --First row, Last row, Middle column (flm) routines; these are based on the bit pattern ops. above chrtable= "abcdefghijklm" --General line test results, characters a to m (in order) --These give certain bit patterns for certain characters different 'weights' - in what seems to --be the hex values, the alpha (A to E) is the bit pattern (see valtable below) and the numeric --coefficient is the likleyhood, 1 being certain and 9 being most uncertain. --This is loosely based on the idea of neural networks, where, given a set of input events --Table entries have the format: --where "xnxnxn -- xnxnxn" corresponds to: -- --"(likeleyhood)(fstrowpat4 l) (likeleyhood)(lstrowpat4 l) (likeleyhood)(middlepat l) --(likeleyhood)(fstrowpat3 l) (likeleyhood)(lstrowpat3 l) (likeleyhood)(middlepat l)" pattable = ["2B2A1C2B1B9C", "1A1B1B001B1B", "3B2A1B3B2A1B", "1A2B2B2A1B2B", "2B2A1C2B2A1C", "2A2A2B2A2A2B", "1B1B1D1B1B1D", "1A1B1A2A1B1A", "2E1A1B2E1A1B", "2E1B1B1A1A1B", "1A1B2B9B2B2B", "1A1A1A1A1A1A", "2C2C1A2C2C1A"] --Values from A to E valtable = ["010", "01010", "0101010", "010101010", "0"] --Finds out what code the is for a particular bit pattern wotami:: [Char] -> Char wotami c |c == [] ='E' |c == valtable!!0 ='A' |c == valtable!!1 ='B' |c == valtable!!2 ='C' |c == valtable!!3 ='D' |c == valtable!!4 ='E' |otherwise ='E' --Get a set of values from the pattable, get the given values from the flm functions, output --a value (0 is not at all, 1 being most likely) compre:: [[Char]] -> [Char] -> Float compre l p |l==[] =error "" |otherwise =compre_a lstvals p where lstvals=[wotami ((finalpat4 l)!!0)]++[wotami ((finalpat4 l)!!1)] ++[wotami ((finalpat4 l)!!2)]++[wotami ((finalpat3 l)!!0)] ++[wotami ((finalpat3 l)!!1)]++[wotami ((finalpat3 l)!!2)] --Check list of flm results against the results of the pattern table; output a floating pt. value --This is the likelihood that a bit pattern matches a character, 1 being most likely, 0 being least --likely. compre_a:: [Char] -> [Char] -> Float compre_a l p |p==[] =0 |l!!0 == p!!1 =((1.0/6.0)*(1.0/fac)) + (compre_a (tail l) (drop 2 p)) |otherwise =0 + (compre_a (tail l) (drop 2 p)) where fac=fromInt (ord (p!!0) - 48) --A neat wrapper function, that, given the test image, and a list of likely suspects, outputs a list of --floats by testing each character in turn in the list. compreandfloat:: [[Char]] -> [Char] -> [Float] compreandfloat l s |s==[] =[] |otherwise =compre l (pattable!!(elemIndex (chrtable) (s!!0))): compreandfloat l (tail s) --The first reasoning routine to make a stab at guessing the character- give it a test image, --a list of likely suspects, and, then, it guesses the character, comparing the character list with the list of floats, --and thus deducing which character is most likely - eg comparing [0.8, 0.9, 0.7, 0.1] to "abcd" would --output b because it is most likely (of 0.9 likliness). flmmethod:: [[Char]] -> [Char] -> Char flmmethod l s = s!!(elemIndex lst (maximum lst)) where lst=(compreandfloat l s) --STOP PRESS!! STOP PRESS!! STOP PRESS!! STOP PRESS!! STOP PRESS!! STOP PRESS!! STOP PRESS!! STOP PRESS!! --STOP PRESS!! STOP PRESS!! STOP PRESS!! STOP PRESS!! STOP PRESS!! STOP PRESS!! STOP PRESS!! STOP PRESS!! --So how exactly does flmmethod work? --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ --Given a character, with a crust of zeroes around it (to combat serifs): -- -- | -- 000000000 -- 011000000 -- 010000000 -- --->010000000 <- fstrowpat3 = "010" -- 010000000 -- 011111100 -- 010000010 -- 010000010 -- 010000010 -- --->010000010 <- lstrowpat3 = "01010" -- 010000010 -- 011111100 -- 000000000 -- | -- `~~~~~~~~~~~~~ <- middlepat3 = "01010" -- --As illustrated in the diagram above, cutting the characters at different rows (fst and lstrowpat) --and the middle column gives different 'bit patterns', where the bit pattern has a 0 for a length --of 0's and 1 for a length of 1's (remember bitp "00011111000000111100011100" = "0101010"). The --likelihood for any particular pattern for any particular bit pattern in a character is stored in --the pattable table. --These results are mathematically processed, and so flmmethod figures out what the character is --*most* likely to be (more often than not, the character is not exactly (ie probability of 1) --identified). --STOP PRESS!! STOP PRESS!! STOP PRESS!! STOP PRESS!! STOP PRESS!! STOP PRESS!! STOP PRESS!! STOP PRESS!! --STOP PRESS!! STOP PRESS!! STOP PRESS!! STOP PRESS!! STOP PRESS!! STOP PRESS!! STOP PRESS!! STOP PRESS!! --Little test routines --~~~~~~~~~~~~~~~~~~~~ --Does the character have the same weight top and bottom? horizflip:: [[Char]] -> Bool horizflip l = (torbweight l == torbweight (reverse l)) --Does the character have the same weight left and right? vertflip:: [[Char]] -> Bool vertflip l = ((lorrline (transpose(reverse(transpose l)))) == (lorrline l)) --Get top half of character! tophalf:: [[Char]] -> [[Char]] tophalf l =take n l where n= (length l) `div` 2 --Get bottom half of character! bothalf:: [[Char]] -> [[Char]] bothalf l =drop n l where n= (length l) `div` 2 --m test mtst::[[Char]] -> Bool mtst l= (lstrowpat3 (stuffedcrust l) == "0101010") --c test ctest:: [[Char]] -> Bool ctest l= (lorrline l == 'L') && (lorrline (transpose(tophalf l))=='L') && (lorrline (transpose(bothalf l))=='R') --gtest- is it the squiggly two circle g? gtest:: [[Char]] -> Bool gtest l = ((middlepat (stuffedcrust l)) == "010101010") --g or e? is it e? isite:: [[Char]] -> Bool isite l |(lorrline p) == 'L' =True |otherwise =False where p=[l!!(floor ((2.0/3.0)*(fromInt m)))] m=(length (head (transpose l))) --h or k? is it k? isitk:: [[Char]] -> Bool isitk l |(torbweight (transpose (bothalf l))) == 'T' =True |otherwise =False -- --Main testing routines --~~~~~~~~~~~~~~~~~~~~~ --Different levels of reasoning based on the tests. --Splits the likelihood of positive ID based on firstly wether the character is top or bottom --heavy, and then wether it has a line on the left or the right, and then several sub-tests --depending on the group of characters being tested. --Remember, flmmethod is the bit pattern operation, that, given a list of likely suspects and --an input character, tries to figure out what, out of the suspects, the character is. --Firstly, remove h,k,m, or possibly c, e, or g from the tests immediately to speed things up. --Then go onto next levels based on top-or-bottom character weight. level1:: [[Char]] -> Char level1 l |(ratio l <0.8) || (mtst l) ='m' |(ctest l) && (ratio l <=1.4) =flmmethod l "ceg" |hork && (isitk l) ='k' |hork ='h' |((torbweight l) == 'B') =levelB l |otherwise =levelT l where hork = (llrow l=="101") --left or right line side? levelB:: [[Char]] -> Char levelB l |lorrline l == 'L' =levelBL l |otherwise =levelBR l levelT:: [[Char]] -> Char levelT l |lorrline l == 'L' =levelTL l |otherwise =levelTR l --Bottomheavy, leftline levelBL:: [[Char]] -> Char levelBL l |(ratio l <0.8) || (mtst l) ='m' |gtest l ='g' |(ratio l > 1.5) && val =="101" && (ctest l) ='c' |(ratio l > 1.5) && val =="101" ='i' |(ratio l > 1.5) && val == "1" ='l' |(ratio l >=0.8) && lrt && (isitk l) ='k' |(ratio l >=0.8) && lrt ='h' |(ratio l >=0.8) && ((horizflip l)/=True) && ((vertflip l)/=False) ='b' |(ratio l >=0.8) && (vertflip l/=False) && (ctest l==False) ='g' |(ratio l >=0.8) && (vertflip l/=False) ='c' |(ratio l >=0.8) && (ctest l==True) ='c' |otherwise =flmmethod l "bg" where val = middlepat l lrt = ((llrow (stuffedcrust l)) == "01010") --bottomheavy, rightline levelBR:: [[Char]] -> Char levelBR l |(ratio l <0.8) || (mtst l) ='m' |gtest l ='g' |(ratio l > 1.4) && val =="101" && jtest ='j' |(ratio l > 1.4) && val =="101" ='i' |(ratio l > 1.4) && val == "1" ='l' |(ratio l > 1.4) && bheavy && gornot ='g' |(ratio l > 1.4) && bheavy =flmmethod l "dj" |(ratio l >=0.8) && (ratio l < 1.3) && rtsided ='a' |(ratio l >=1.2) && (ratio l <1.4) && rtsided =flmmethod l "dgj" |(ratio l >=0.8) && (rtsided/=True) && (isitk l) ='k' |(ratio l >=0.8) && (rtsided/=True) =flmmethod l "cehkbf" |(ratio l >=0.8) && (isite l/=True) && (isitk l) ='k' |(ratio l >=0.8) && (isite l/=True) =flmmethod l "adghkm" |otherwise =flmmethod l "ce" where gornot= (middlepat (stuffedcrust l) =="0101010") && (fstrowpat4 (stuffedcrust l) /= "010") bheavy= (torbweight l == 'B') && (torbweight (reverse l) == 'T') val = middlepat l jtest =((lorrline (transpose(reverse(transpose l)))) /= (lorrline l)) rtsided= (lorrline l == 'R') hork= ((llrow (stuffedcrust l)) == "01010") --topheavy, leftline levelTL:: [[Char]] -> Char levelTL l |(ratio l <0.8) || (mtst l) ='m' |gtest l ='g' |(ratio l > 1.4) && theavy && (horizflip l==False) ='f' |(ratio l > 1.4) && val =="101" && jtest ='j' |(ratio l > 1.4) && val =="101" ='i' |(ratio l >=0.8) && (ratio l <1.5) =flmmethod l "ce" |otherwise =flmmethod l "acdeghkm" where val = middlepat l theavy = (torbweight l == 'T') jtest = ((lorrline (transpose(reverse(transpose l)))) /= (lorrline l)) --topheavy, rightline levelTR:: [[Char]] -> Char levelTR l |(ratio l <0.8) || (mtst l) ='m' |gtest l ='g' |(ratio l > 1.4) && val =="101" && jtest ='j' |(ratio l > 1.4) && val =="101" ='i' |(ratio l > 1.4) && val == "1" ='l' |otherwise =flmmethod l "cdghkm" where val = middlepat l jtest = ((lorrline (transpose(reverse(transpose l)))) /= (lorrline l)) --Main reasoning routine: ocr:: [[Char]] -> Char ocr l |l==[] =error "No input!" |otherwise =level1 (trim l) --Implementation diary: --2154 25/04/97 Middle of trying to program all my chop functions.... --2259 25/04/97 Finish all those pesky chop routines and wrap up into a neat trim routine. --2300 25/04/97 Drink large amounts of coffee. Continue onwards..... --1533 03/05/97 Stop stressing over Physics and get on with ocr version 2. Stuff all the right and -- left trims and just use transpose. --1600 03/05/97 Serifs are stupid. Serifs are stupid. Serifs are stupid. Serifs are stupid. -- Serifs are stupid. Serifs are stupid. Serifs are stupid. Serifs are stupid. -- Serifs are stupid. Serifs are stupid. Serifs are stupid. Serifs are stupid. -- Serifs are stupid. Serifs are stupid. Serifs are stupid. Serifs are stupid. --1611 03/05/97 BRAINSTORM!!! Idea: Those pesky serifs are really irritating, buggering up my -- newly written bit pattern functions. Solution- put a thin crust of zeroes around -- trimmed character, thus eliminating those stupid serifs. --1716 03/05/97 Cool. No more worries with those irritating serifs. On to next version (my bit -- pattern function is producing close to consistent results) and to 'hole' -- and 'open hole' detection (will it work?). --1300 04/05/97 Written some test functions to output various test data --1632 04/05/97 After some manual paperwork on the results of the bit pattern functions, figure -- that I need some functions to identify vertical or horizontal lines. On to -- version 4. --1804 04/05/97 Got the function to identify vertical lines operating perfectly. Idea: function -- to find the greatest density of characters- top or bottom of the character (eg -- for seperating f from j, g from a). Up to 333 lines of code so far! --1906 05/05/97 Time to panic. Aim to complete flm functions and then complete (most) of the -- reasoning routines. --2147 05/05/97 Completed first reasonig routine and tweaked ratio and flm and thingy values; it -- seems to work. At this point, identifying 47 images (60.26%). Possible improvments: -- run ratio with less importance; better differentiation between i, j, f and k. --2216 06/05/97 After some more tweaking, the routine detects 10 more images (57 which is @ 76%), -- suggested improvments include writing a routine which checks the symmetry of -- characters and something that identifies (reliably) those confounded k's. --2323 08/05/97 Do some major fiddling with the rule set and get a different set of values; get -- accuracy of 86% with my own set of characters combined with the given set; get an -- accuracy of 76% with someone else's set. --1503 11/05/97 Time to panic part II. Fix up all the function names and execution steps; write a -- funky little 'c' ID function, fix up all them 'g' ID functions, and all will be -- well with the world (grumble grumble grumble grumble grumble grumble grumble) --1801 11/05/97 Finally get out a working version of my program that I am happy with. Get above 80% -- recognition with other peoples character sets, 95% with the original character set. -- Probably a good time to fix up some of the more vauge function names!(@version 13) --Testing: --The first testing that occured was simply testing the trim/chop functions are actually working --by verifying the routine with characters out of letters.hs and the view function. -- --Then, all the bit patten operations, line and density tests were tested by mapping the characters --to the functions and writing the results to a file (see functions lr?, fv etc. below to get a --rough impression of how this was done). Once these results were ascertained and written to a file, --a logic tree was constructed and translated into the first 'levels' of reasoning. These reasoning --levels were converted into haskell functions and put behind the first 'ocr' function. From this --point onwards, the majority of the testing was done with the 'test ocr' command. Where it became --obvious that a particular group of characters were being mis-identified, special functions were --written to deal with the offending group (eg to differentiate between h and k). -- --To complicate things even further, I composed a small library of test characters and traded with --others so that I eventually had a small but varied library of test characters to use. These extra --characters were also used to test my ocr function, and allowed me to tweak various parts of the --reasoning routines so that they worked with a large character set (so that they worked for 'most' --characters (there was no point in going for 100% in one set only to get 60% in another!). -- --Generally speaking, most testing and debugging occured by running the text editor and hugs --simletaneously, giving a much better 'feel' to testing and it made the smoothing out of --bugs much less complex. --Test letter b = [ "00000000000000000000", "00000000000000000000", "00000000000000000000", "00000000000000000000", "00000010000000000000", "00000010000000000000", "00000010000000000000", "00000011111100000000", "00000010000010000000", "00000010000010000000", "00000010000010000000", "00000010000010000000", "00000010000010000000", "00000011111100000000", "00000000000000000000", "00000000000000000000", "00000000000000000000", "00000000000000000000", "00000000000000000000", "00000000000000000000" ] --ti = [a0, a1, a2, a3, a4, a5, b0, b1, b2, b3, b4, b5, c0, c1, c2, c3, c4, c5, -- d0, d1, d2, d3, d4, d5, e0, e1, e2, e3, e4, e5, f0, f1, f2, f3, f4, f5, -- g0, g1, g2, g3, g4, g5, h0, h1, h2, h3, h4, h5, i0, i1, i2, i3, i4, i5, -- j0, j1, j2, j3, j4, j5, k0, k1, k2, k3, k4, k5, l0, l1, l2, l3, l4, l5, -- m0, m1, m2, m3, m4, m5] --til = ["a0", "a1", "a2", "a3", "a4", "a5", "b0", "b1", "b2", "b3", "b4", "b5", -- "c0", "c1", "c2", "c3", "c4", "c5", "d0", "d1", "d2", "d3", "d4", "d5", -- "e0", "e1", "e2", "e3", "e4", "e5", "f0", "f1", "f2", "f3", "f4", "f5", -- "g0", "g1", "g2", "g3", "g4", "g5", "h0", "h1", "h2", "h3", "h4", "h5", -- "i0", "i1", "i2", "i3", "i4", "i5", "j0", "j1", "j2", "j3", "j4", "j5", -- "k0", "k1", "k2", "k3", "k4", "k5", "l0", "l1", "l2", "l3", "l4", "l5", -- "m0", "m1", "m2", "m3", "m4", "m5"] --Testing routines! fv:: [[Char]] -> [[Char]] fv l = finalpat3 (trim l) --example use- putStr (lr (map fv ti) til), for analysing bit patterns --or- writeFile "cool" (lr (map fv ti) til) lr a b |a==[] =[] |otherwise =(h!!0 ++ " " ++ h!!1 ++ " " ++ h!!2 ++ " " ++ head b ++ " \n" ) ++ (lr (tail a) (tail b)) where h=head a --example use- lr2 (map show (map ratio ti)) til, for analysing ratios --or- writeFile "cool2" (lr2 (map show (map ratio ti)) til) lr2 a b |a==[] =[] |otherwise =(head a ++ " " ++ head b ++ " \n") ++ (lr2 (tail a) (tail b)) -- putStr(lr3 1 ti til), for isolating a particular ratio of characters. lr3 x l ll |l==[] =[] |(ratio (head l)) > x =("True " ++ " " ++ (head ll) ++ " \n") ++ (lr3 x (tail l) (tail ll)) |otherwise =("False" ++ " " ++ (head ll) ++ " \n") ++ (lr3 x (tail l) (tail ll)) --example use- putStr (lr4 (map torbweight (map trim ti)) til), for anyalysing blobs lr4 a b |a==[] =[] |otherwise =([head a] ++ " " ++ (head b) ++ " \n") ++ (lr4 (tail a) (tail b))