How to run Fit from the JaM interface Maureen Stone, May 30, 1985 To get the curve-fitting software bringover -p /cyan/imaging/top/fitjam.df. This will bringover the curvefitting code, the Imager and JaMImager. Type JaMImager to the CommandTool. Once both windows open up type (Fit) .run init to the JaM window. This will load the curve-fitting software. The process of fitting an image is in roughly three steps: converting the AIS file to sample points, selecting potential joints from the sample points, and selecting the actual joints from the potential joints. Finding the sample points The module ContourJaM contains the commands for converting a variety of sampled images to a sequence of sample points. The first set of operations take a file description and produce a pixel array. For AIS files: (name) Contour.openAIS, Contour.closeAIS For AC fonts: (name) Contour.openACFont, (char) Contour.getACChar Once you have a pixel array you may use: Contour.showPA %display the pixel array in the JaMImager window x1 y1 x2 y2 Contour.windowPA %set a subwindow on the pixel array Contour.showWindow %draws an outline of the window Contour.resetWindow %resets the window to the size of the pixel array Two types of edge finders are available for finding the edges in a pixel array. The first involves finding all samples at a particular threshold value and is the correct one to use for AIS files. The threshold value must not be exactly a pixel value ie. use 128.5, not 128.0. White is 255, black is 0 so setting the threshold higher includes lighter grey pixels inside the contour and vise versa. The second set includes two routines for finding samples in bitmaps. value Outline.tvalue % set the black/white boundary somewhere between 0 and 255 .outline % find sample points at current tvalue .outlineBlackEdge %one pixel produces 4 points in a 1 by 1 square .outlineBlackCenter % 4 pixels produces 4 points in a 1 by 1 square The edge finders will scan through the pixel array in bottom to top order, picking out the edge points. Once the entire array (or window) is scanned the samples are arranged into contours, each one a loop of sample points. For example, the letter "S" would produce one loop of samples that described the outline of the S. The letter ":" would produce two loops, as would the letter "A". The outlining procedures will draw the edges in the JaMImager window as they find them. They will leave the total number of contours on the stack. To run the curve fitting software the samples must be copied to the curve fitting data-structures. There are two commands to do this number Outline.setsa %copy numbered contour (contours are numbered in order of discovery) Outline.setallsa %copy all the contours There are two more commands you need to know about here number .setslen % minimum distance between samples .true .closed %indicate that the contour is closed From a scanned image the minimum distance between samples is normally .04. It is often preferable to filter out a subset of these samples. The operation ".setslen" sets a minimum sample distance on the curve-fitting data structure, so you should set it before doing Outline.setsa or Outline .setallsa. Another property of the curve-fitting data structure is whether the set of sample points represent an open or closed contour. This property is set with the ".closed" command. So, a typical set of commands for fitting getting the samples from an AIS file are: (Logo.ais) Contour.openAIS 128.5 Outline.tvalue .outline % adjust the tvalue here if the outline doesn't fall where you want it .1 .setslen % a typical value. Use up to 1 or 2 if the picture is very noisy Outline.setallsa % copy all the contours at once to the curve-fitting software .true .closed %indicate closed contours Contour.closeAIS The Curve-fitting data structure The curve fitting uses a data structure that is a linked list of contours. Each contour is a list of samples and a list of cubic pieces that are the result of the latest fit to those samples. FitJaM keeps one such data structure and supplies the following commands to manipulate it (see FitEditJaM, FitIOJaM and Fit.JaM for a more exhaustive list). Many of the commands affect only the current contour in the data structure ie. one list of samples/cubics .nextcon %go to the next contour ms %mark all the samples in the current contour with a little square ds %display all the samples in the current contour as a polygon dc % display all the cubics in the current contour mc % mark all the joints between the cubics in the current contour bool .closed %set the closed flag number .setslen % minimum distance between samples number .setscale %scale all the values by this amount when you display them x y .setoffset %offset all the values by this amount when you display them Finding potential joints A complicated shape will contain a number of cubic pieces. While in theory it would be possible to test every sample point as a potential joint (knot), this would make the curve-fitting step far too expensive. Another consideration is whether to enforce smoothness at the joints. Outlines often contain both smooth curves and corners. The smoothness is controlled by setting the tangent vectors entering and leaving the potential joint. If they are the same, the joint will be smooth. If they are different, the joint will be a corner. Fit supports both optional and forced joints. A forced joint is guarenteed to be a joint after the curve fitting step. The module PotentialKnotsJaM contains a set of routines for selecting potential joints and controlling their tangents. First, use ds or ms to look at your sample points. If they look ragged or noisy, you might want to apply a filter to smooth them. Note that a filter such as this one will move the samples slightly, rounding corners and shrinking the contour slightly (repeated applications would move all the samples towards a point at the center of the contour). At this time, the only way to keep a sample from moving is to mark it as a forced joint. The boolean parameter to the command indicates whether forced joints are included in the filtering. The command (from FiltersJaM) is: bool .avefilter There are several routines available to set potential joints. Most of them involve finding lines and corners. The rule is that each routine will only add potential joints. Many of them set tangent values at the same time. The rule for setting tangents is that no routine will set a tangent if there is already a tangent value set. For the truth, see PotentialKnotsJaM .resetjoints %clear current joints val .resettangents %val=0 reset all tangents, %val=1 reset on potential joints only, val=3 reset on forced joints only penalty lineLength maxAngle PK.dynpoly %Finds nodes using DynFit. IF two adjacent lines are each longer than lineLength AND the turning angle is >= maxAngle THEN force a corner at the intersection. maxAngle setCorners PK.quicktangents % computes tangents by differencing neighbors. Corners are defined when the turning angle is >=maxangle. setCorners=TRUE sets one-sided tangents on corners, otherwise leaves them free. maxAngle setCorners PK.squaretangents %like PK.quicktangents except uses squared differences maxangle setCorners PK.circletangents %computes tangents, using circle through three adjacent potential joints. Parameters like PK.quicktangents. bool PK.hvextremes %sets potential knots on global extremes. TRUE=force joints maxangle PK.nearlyequal %sets nearly equal left and right tangents the same maxangle PK.forcecorners %forces corners if turning angle between two tangents is > maxangle minL, minFlat, forceKnots PK.find90 %IF two flat lines of minL meet at a corner, mark it. Assume flat lines are horizontal or vertical (only cases that will in practice pass the flatness test for small values of minFlat long maxangle forceknots PK.hvlines %Puts potential knots around long horizontal and vertical lines. maxangle is the tolerance for the decision and is the turning angle between adjacent samples (typically about .01) Finding the curves Once all the potential joints and tangents are set, there are three ways to select among them for the best fit. All three are implemented in PiecewiseFitJaM. There are 4 error metrics that can be set to controll the fitting. The first, maxItr, is the maximum iterations used in the inner fitting loop. maxDev, the maximum deviation is used in both the inner and outer fitting loops. sumErr and deltaT affect only the inner loop. maxItr maxDev sumErr deltaT .metrics %sets the metrics globally .fit %make each potential joint and actual joint .grow %grow out a spline curve until it's maxDev exceeds the one in the metrics penalty trim .dynspline %dynamic programming. Badness= sum of maxDev+penalty if maxDev is greater than that in the metrics. trim is a boolean, which should almost always be true. Some examples FitJaM.df contains AMR.JaM. This demonstrates my current set of operations for 1200 spi AC fonts. This is (I think) what I used to fit the dragon image. I used a 1 .setslen to filter out some of the points. (doDragonO) {.false .avefilter .false .avefilter 1 dynpoly 89 .true PK.quicktangents .false PK.hvextremes 5 2 0 0 .metrics .grow}.cvx .def Κ*˜head˜%K˜Iblockšœ˜Οb œ5œB˜§L˜Σšœ˜˜ΖLšœΟiœ#˜7Lšœžœžœ˜A—˜)Lšœ?˜?Lšž œ6˜ALšœ2˜2LšœF˜F—˜ΧLšžœJ˜OLšœ/˜/LšœB˜BLšœE˜E—Lšœ΄žœή˜ššœ…˜…LšžœU˜[Lšœ(˜(—˜7Lšžœ,˜2L˜2—šœΆ˜ΆLšœ˜Lšœd˜dL˜MLšœN˜NL˜'L˜——˜ ˜ΙL˜ L˜DL˜?L˜vLšžœ˜!Lšžœ-˜3LšžœE˜KLšžœH˜K——˜L˜šœΎ˜ΎLšžœ ˜—˜τL˜"Lšžœu˜xLšžœž œžœ¬˜ΗLšžœΝ˜ΰLšžœI˜\Lšžœ˜’LšžœΟkœ7Ÿœ ˜OLšΟcœC˜KLš œT˜\Lš œΓ˜άLš œΐ˜Ψ——˜˜²LšžΠciž‘œ$˜?L˜0LšœO˜OLš  œ§˜΄——˜L˜bLšœm˜mJ˜ŠL˜˜L˜————…—$f&–