Binary Texture Analysis Stuart Smith [email protected] Introduction Many fabric and wallpaper designs are tessellations based on a square or rectangular tile. If such a design is represented as a binary image, Langlet’s Helical and Cognitive transforms [1] can “de-tessellate” simple cases, revealing the tile. Even if the tile is obscured by a significant amount of noise, an approximation can still be recovered by methods that rely on “extended exclusive-or,” which comprises ≠ , ≠ /, and ≠ \ [2]. APL functions for experimentation with texture analysis are listed in the Appendix, and the complete texture workspace is available at www.cs.uml.edu/~stu/textile.dws. Basic Texture Analysis The simplest case of binary texture analysis involves a square binary image composed of identical square tiles. Suppose, for example, we are given the patch of texture shown in fig 1. On casual inspection it is obvious that it is made up of repetitions of some smaller pattern. What is that pattern? Figure 1. Texture swatch composed of identical square tiles If the patch is square with side a power of 2 (fig.1 is 256×256) the two-dimensional Helical transform (HEL2) may be able to identify the tile. The result from HEL2 is shown in fig. 2. As can be seen, there is a 16×16-pixel subimage in the upper left corner of an otherwise blank square.1 This result confirms that the swatch is composed of identical 16×16 tiles.2 The original 1 The Cognitive transform (COG2) can also be used, in which case the subimage will appear in the lower right corner. 2 If even one pixel in one tile had differed from the pixels in the same position in the other tiles, the transform would have been “smeared” over the entire 256×256 result matrix. Figure 2. Result of applying HEL2 to fig. 1 tile may be obtained simply by extracting the 16×16-pixel subimage at, for example, any of the four corners of the original image. Extracting a Tile From a Noisy Texture Image When a tessellation is not composed of exact copies of a tile but rather contains a certain amount of noise (i.e., random black-to-white and white-to-black reversals of pixel color) it is more difficult to analyze. In such cases a different method is required. The first step is to determine the dimensions of the tile. This is accomplished with the correl function, which performs a sort of circular autocorrelation. Suppose we are given the image of Fig. 3, which contains about 12% noise over the whole image. Figure 3. 256×256 tiled image with 12% corrupt pixels correl determines periodicity in both the horizontal and vertical directions and, optionally, displays a plot showing the periods. The plot in Fig. 4 shows a peak every 16 steps, and the function reports that it has determined that the period is 16 in both the horizontal and vertical directions. Thus, the image shown in Fig. 3 must be composed of 16×16 tiles. Figure 4. correl plot showing the periodicity of Fig. 5 Once the dimensions of the tile have been determined, the image can be broken up into tile-sized subimages. Each subimage is then used to make a test image the same size as the image being analyzed. Finally, each test image is exclusive-OR’ed element-wise with the original image, yielding a difference image. The tile that produces the difference image containing the smallest number of 1’s is taken to be the closest estimate of the original tile. find_tile does all the required operations. In general, if an image has some given percentage of random pixel color reversals, each tile will also contain about this same percentage of affected pixels. This means that we cannot be certain exactly what tile was used to create the image.3 Non-Square Swatches and Tiles find_tile does not require either the tile or the whole image to be square. Fig. 5, for example, is a computer-generated, but realistic, example of fabric texture. The estimated tile calculated by find_tile is shown in fig. 6. correl will correctly determine periodicity in the presence of ≥45% noise. At these levels an estimated tile is too noisy to be usable as an approximation of the actual tile used to construct an image. 3 Figure 5. Computer-generated swatch with 15% noise pixels Figure 6. Zoomed-in view of the estimated tile Real-world Texture Analysis With just find_tile texture analysis is restricted largely to the kind of well-behaved problem exemplified by figs. 1, 3, and 5. A real-world challenge is to invent functions that can handle a binary image like fig. 7, which was made from a photograph of an actual swatch of fabric woven in the herringbone pattern. The current correl function is unable to determine the horizontal periodicity of this swatch even though to the eye it is obvious that the pattern is repeated four times across the image. Figure 7. Binary image of a swatch of herringbone weave fabric One could take a conventional image processing approach to the analysis of the image in fig. 7; however, to do so would be to abandon the approach described here. The motivation for at least trying to stay within the boolean framework is the utter simplicity of the key operations, most of which can be realized as APL one-liners. The Texture workspace contains the following variables, which are images of actual fabrics: bricks, circles, hound, llamas, swirls, and zigzag. These are included to allow experiments that demonstrate what the analysis functions can and can’t do with real fabric patterns (see pictures of the fabric swatches in the Appendix). Roll Your Own The function testmake allows you to construct your own test images to experiment with. For example, start by creating a tile with a random pattern: tile←8 12 xrandm 0.5 or a tile with rotational symmetry, e.g.: tile←8 12 sympat2 0.5 In either of these cases, tile will be an 8×12 matrix of 1’s and 0’s with approximately equal numbers of each. Next, create a tessellated pattern that by default contains about 12% noise: z←4 7 testmake tile z will be a 4×7 array of tile partially obscured by noise. With correl, check to make sure z’s periodicity can be determined : 0.9 correl z ⍝ 0.9 is the matching tolerance Finally, get the estimated tile with x←find_tile z The result can be written out to a file with, e.g., writebmp. The implementation-dependent function, disp, can be used to directly visualize the result. The quality of the estimated tile can be given by a simple metric like (+/+/x=tile)÷×/⍴ tile, 1 being a perfect match. The amount of noise a test image contains is determined by the right argument to xrandm in the definition of testmake. It is set there to 0.12 (i.e., 12%), but it can generally be set somewhat higher. Possible experiments include varying this value and observing whether find_tile succeeds in finding a good approximation to tile. Another type of experiment illustrates the effect of varying the relative number of white vs. black pixels in the tile. This is done by varying the right argument of xrandm, sympat, sympat2, sympat4, or sympat4r between 0 and 1. A clean tessellation can be created with the textile function. Tiles can be made with xrandm, sympat, sympat2, sympat4, and sympat4r, as shown above, or selected from a small set of tiles included as the variables m1-m7 in the Texture workspace. For example, a 3×3 tiled pattern using m7 can be made with x←3 3 textile m7 Pictures of the seven tiles can be found in the Appendix. These tiles can of course also be used with testmake to create tessellations obscured by as much or as little noise as desired. References [1] Gérard Langlet. Towards the Ultimate APL-TOE. ACM SIGAPL Quote Quad, 23:1, July 1992. [2] Michael Zaus. Crisp and Soft Computing with Hypercubical Calculus: New Approaches to Modeling in Cognitive Science and Technology with Parity Logic, Fuzzy Logic, and Evolutionary Computing: Studies in Fuzziness and Soft Computing, Vol. 27. Physica-Verlag HD, 1999. Appendix Helical transform of square logical matrix ⍵ whose side is a power of 2 HEL2←{⍉↑ HEL¨↓ ⍉↑ HEL¨↓ ⍵ } Cognitive transform of square logical matrix ⍵ whose side is a power of 2 COG2←{⍉↑ COG¨↓ ⍉↑ COG¨↓ ⍵ } Helical transform of logical vector ⍵ whose length is a power of 2 HEL←{ (⍴ ⍵ )=1:⍵ (∇ lhalf ⍵ ),∇ (lhalf ⍵ )≠ rhalf ⍵ } Cognitive transform of logical vector ⍵ whose length is a power of 2 COG←{ (⍴ ⍵ )=1:⍵ (∇ (rhalf ⍵ )≠ lhalf ⍵ ),∇ rhalf ⍵ } Auxiliary functions required by the transforms lhalf←{((⍴ ⍵ )÷2)↑ ⍵ } rhalf←{((⍴ ⍵ )÷2)↓ ⍵ } Find the tile of tessellation img, a rectangular matrix of arbitrary dimensions z←find_tile img;r;c;pats;R;C;rows;cols;k;i;bgr;res;tot;min r c←⍴ img pats←cells img R C←⍴ ⊃ pats[1] ⎕←R,C rows cols←(⌊ r÷R),⌊ c÷C ⎕←rows,cols k←⍳ ⍴ pats min←r×c :For i :In k bgr←r c↑ (rows,cols)textile⊃ pats[i] res←bgr≠ img tot←+/+/res :If tot<min min←tot z←⊃ pats[i] :EndIf :EndFor cells←{ ⎕IO←0 ⎕ML←0 DIM←⍴ ⍵ dim←0.9 correl ⍵ r←1+0⌷ dim c←1+1⌷ dim R←0⌷ DIM C←1⌷ DIM ,⍉↑ (⊂ R⍴ r↑ 1)⊂ [0]¨(C⍴ c↑ 1)⊂ [1]R C⍴ ⍵ } Find the vertical and horizontal periods of binary image y correl←{ ⎕IO←1 ht←shad ⍵ hmax←⍺ ×⌈ /ht h←(ht≥ hmax)/⍳ ⍴ ht h←1↑ (h≥ 4)/h ⍝ vt←shad⍉⍵ vmax←⍺ ×⌈ /vt v←(vt≥ vmax)/⍳ ⍴ vt v←1↑ (v≥ 4)/v ⍝ ⍝ Optional correlation plot ⍝ vt matplot ht ⍝ v,h } z←shad y;⎕IO;s;shf;n;i ⎕IO←1 s←2⌷ ⍴ y z←⍳ 0 shf←y n←⌊ s÷2 :For i :In ⍳ n shf←¯1⌽shf z←z,+/+/shf=y :EndFor matplot h;⎕USING;sp;vw ⎕USING←',sharpplot.dll' ',system.drawing.dll' ⍝ Bring in the ⍝ .Net namespaces needed by SharpPlot ⍝ create a chart sp←⎕NEW Causeway.SharpPlot sp.FrameStyle←Causeway.FrameStyles.Boxed sp.Heading←'Correlation' sp.SetKeyText⊂ 'Vertical' 'Horizontal' ⍝ enclosed because a ⍝ single argument of two strings sp.YAxisStyle←Causeway.YAxisStyles.(ForceZero+PlainAxis) sp.SetYTickMarks 2 1 ⍝ two arguments here (major internal and ⍝ minor interval), so no enclose sp.DrawLineGraph⊂ v h ⍝ again, there is a single argument which ⍝ is a list of lists of y values - (sp.DrawLineGraph v h) would ⍝ take v as y values and h as x values ⍝ view it vw←⎕NEW Causeway.SharpPlotViewer vw.SharpPlot←sp vw.Show ⍬ Create a tiling whose dimensions are ⍺ with tile ⍵ (two different versions) textile←{⍎⍤ 1⍕⍺ ⍴ ⊂ ⍵ } textile←{⊃ ⍪ /,/⍺ ⍴ ⊂ ⍵ } Display array bits disp bits 'bm'⎕WC'bitmap'('Bits'bits)('Cmap'(2 3⍴ 255 255 255 0 0 0)) 'f'⎕WC'form'('Picture' 'bm') Write array bits to .bmp file x bits writebmp file;bm 'bm'⎕WC'bitmap'('Bits'bits)('Cmap'(2 3⍴ 255 255 255 0 0 0)) bm.File←mypath,file 2 ⎕NQ'bm' 'FileWrite' Read .bmp file file into array argb argb←readbmp file;bmp 'bmp'⎕WC'Bitmap'(mypath,file,'.bmp') argb←255=256 256 256⊤ bmp.CBits argb←argb[1;;] z←mypath z←'C:\Users\your user name\Desktop\' Create tessellations for experiments (approximately 12% noise across each image) testmake←{ t←⍺ textile ⍵ bad←(⍴ t)xrandm 0.12 t≠ bad } z←x xrandm y;n n←×/x z←x⍴ (n?n)≤ y×n z←dim sympat p;t t←⌈ dim÷2 z←t xrandm p z←z,⌽z z←z⍪ ⊖z z←dim sympat2 p;t t←⌈ dim÷2 z←t xrandm p z←z,⌽z z←z⍪ ⊖z z←dim sympat4 p;q1;q2;q3;q4 q1←(⌈ (dim,dim)÷2)xrandm 0.5 q2←rot90 q1 q3←rot90 q2 q4←rot90 q3 z←(q2,q1)⍪ q3,q4 z←dim sympat4r p;q1;q2;q3;q4 q1←(⌈ (dim,dim)÷2)xrandm 0.5 q1←q1=⍉q1 q2←rot90 q1 q3←rot90 q2 q4←rot90 q3 z←(q2,q1)⍪ q3,q4 z←rot90 m;i dim←⍴ m z←(⌽dim)⍴ 0 :For i :In ⍳ dim[1] z[;i]←⌽m[i;] :EndFor Tile motifs included as variables in textile.dws: m1 m2 m3 m4 m5 m6 Fabric patterns included as variables in the Texture workspace bricks circles hound swirls zigzag llamas m7
© Copyright 2026 Paperzz