From: erich@eye.com (Eric Haines)
Newsgroups: comp.graphics,sci.image.processing
Subject: Summary - creating seamless texture map tiles
Date: Fri, 25 Sep 92 16:18:30 GMT
Summary: yes, it is
Organization: 3D/EYE, Inc.  Ithaca, NY

A number of weeks ago I asked how people make seamless textures, i.e. texture
tiles which you can repeat and not notice where the tiles meet.  Thanks to all
who responsed; here they are in no particular order.  If anyone has anything
to add, please do jump in!

Eric Haines

My original posting:

>How are seamless tiles of a texture created?  Various companies sell texture
>libraries that include texture tiles that can be repeated and no seams are
>visible.  For example, given a scanned patch of grass, how can this be
>processed so that the resulting image can be repeated across a surface and not
>have the image edges noticeable?
>
>I haven't seen much on this in the literature (which hopefully means I'm not
>looking at the right literature, eh?).  This question is easily answered if
>you're generating synthetic textures - you simply attach the edges
>(topologically, a torus) so that the thing is generated seamlessly.  But given
>a scanned texture you don't have this ability.  Simply blurring the edges
>helps a little, but you can see the blur.  I can imagine you might use Burt's
>pyramidal parametrics (I played around with "pyrmask" in the Utah Raster
>Toolkit a bit, without great results) or doing some sort of pair of
>convolutions, but this is where my image processing knowledge tends to give
>out.


Tor Lonnestad (tor@ifi.uio.no) writes:

One way of making textures larger than the scanned original, is to convert the
original into a feature vector, and then synthesize a new image with the
desired size from this feature vector.  We have done some experiments on this,
and the best results were generally obtained with the autoregressive model,
see e.g.

   Delp, Kashyap and Mitchell:
   "Image data compression using autoregressive time series models"
   Pattern Recognition Vol.11, 1979, 313-323.

[...]

You could have a look at nearly anything written by Gagalowicz and Ma
(eventually Ma and Gagalowicz!). Here are some:

Ma and Gagalowicz:
"Natural textures synthesis with the control of the autocorrelation
and histogram parameters"
Proc. 3rd Scandinavian Conf. on Image Analysis, Copenhagen, Denmark,
1983, 79-84

Gagalowicz and Ma:
"Model driven synthesis of natural textures for 3D scenes"
Comp. & Graphics, Vol.10, No.2, 1986, 161-170

Also interesting are the spectral, fractal-based methods of Lewis:

Lewis, J.P.:
"Generalized Stochastic Subdivision"
ACM Transactions on Graphics, Vol.6, No.3, 1987, 167-190.

In principle, nearly every texture model and synthesis algorithm could
be used, but when it comes to practical tests - including estimating the
parameters of the model from real images - it becomes evident that only
few models are powerful enough to model an interesting set of the
possible textures.



Benjamin Zhu (zhu@graphack.asd.sgi.com) writes:

You asked about seamless texture tiles on the net.  I will not pretend to be a
texture expert.  Neither am I terribly familiar with image processing.  Just a
little thought here.

You can artificially attach opposite ends of a texture tile, convolve, and
resample at the boundary.  This will give you a more reasonably-looking tile.

I doubt that will be sufficient though.  So you might want to decompose the
texture into different frequencies, convolve and resample at boundary for each
frequency, and then sum up components from all frequencies to form a new
texture.  To decompose texture, you just use mip-maps, and subtract adjacent
mip-maps.

In general, this will necessarily make texture more blurred at the boundary.
If a texture does not match at opposite ends, then there are discontinuities
(think a texture tile as a periodic function).  These discontinuities are just
high frequencies.  By convolving at the boundary, we are simply trying to
eliminate these high frequencies.  We also lose certain features consequently.



Peter Johansson (prj@lysator.liu.se) writes:

I can't think of any general method but in the case of grass and
ordered textures one idea is to filter with an anisotropic filter i.e.
smoothing in one direction. The direction could be estimated from the
texture it self or selected by the user. There is a general method of
image enhancement developed by prof. G. Granlund (and/or) H. Knutsson
here at LiTH in Sweden that uses similar ideas. I'll see if I can find
any good references to that.

[...]

If you have some kind of nonordered texture like granite then it might
look good to let the coordinates reverse at the end and decrease back
to 0 instead of jumping. Eh, not a good explanation. I mean letting
the texture coordinates follow a triangle shape instead of sawtooth
like x going 1,2,3,4,5,4,3,2,1,2,3,... and same to y. Ah, now I know
how to explain it, copy mirrored images of the texture and align them
to the edges, then there's perfect match.

I think it's time to go home and dream sweet dreams about texture
alignment and look for interesting references on image enhancement in
my literature at home tomorrow...

...more...

The reference I had in mind was
H. Knutsson, R. Wilson, and G.H. Granlund. Anisotropic non-stationary
image estimation and its applications - part I: Restoration of noisy
images. IEEE Trans on Communications, COM-31(3):388-397, March 1983

But I haven't read it yet because in the course that I attended in
Computer Vision we had a compendium that had a shorter explanation of
the principle building on previous knowledge that we had and that in
turn referred to the above mentioned article.

I'm not so sure that this is what You are looking for, because the
main idea in that article is problably how to estimate linear (i.e.
one-dimensional) structures in the image and from that and som other
estimations select an appropriate filter to enhance the image.

I guess that what's interesting for you are convolution kernels for the
filters that do anisotropic filtering. It's enough with three
convolution kernels to be able to filter in any direction. The spatial
frequency response of those filters are (in polar coordinates):

              1-cos(pi*rho/1.8)^2        , when 0   <= rho <= 0.9
F1(rho,phi)=  1                 ,        , when 0.9 <= rho < pi - 0.9
              cos(pi*(rho-pi+0.9)/1.8)^2 , when pi-0.9 <= rho < pi             

F2(rho,phi)= F1(rho,phi)*cos(2*phi)

F3(rho,phi)= F1(rho,phi)*sin(2*phi)

and the anisotropic filter in direction phi0 is

F(rho,phi)=0.5*(F1(rho,phi)+cos(2*phi0)*F2(rho,phi)+cos(2*phi0)*F3(rho,phi))

All that magic mathematics is derived from that the desired angular
variation of the anisotropic filter is assumed to be cos(phi-phi0)^2
and using classical trig. (cos(phi)^2=0.5*(1+cos(2*phi))). The radial
variation of the filter is as in F1(rho,phi) (F1 is independent of
phi).

To get convolution kernels to feed into appropriate ppm command it is
possible to merely inverse fourier transform a discretisized version
of the function (discretisized in cartesian coordinates) but there are
better methods (but more complicated) of getting optimal kernels with
limited size of the kernel.

If You are curious enough and have a lot of time to spend on it i
think you have enough info to play around with now, but I will try to
do some experimenting myself and return with more useable info later
on, like ready-optimized kernels that You just multiply with the
factors for the desired direction (cos(2*phi0) and sin(2*phi0)) to get
kernels to enter in your favourite image processing software and som
results on how it is working.

[...]

Oops, I made a little mistake. The filter that i described is the
highpass part of the anisotropic filter. The idea is that you want to
do low-pass filtering in general and where it is linear structures
you want to keep that by adding the (anisotropic) highpass filter to
some amount according to the confidence in the estimation results.

The lowpass filter used is
FLP(rho,phi) = cos(pi*rho/1.8)^2

The factor that controls the amount of directional filtering is called
alpha and the total filter is
Ftot(rho,phi)=F(rho,phi)*alpha + FLP(rho,phi)

It might be useful to keep the parameter alpha to be able to do a
reasonable tradeoff between blurring and directional filtering.

Another, somewhat crazy, idea is to do fractal coding of the texture
image and when decoding it make the picture larger than original. But
that isn't so good because then you have to store the big image
somewhere anyway and some of the point of using (small) texture
images is lost. I'm not sure about how the "enlarged" picture will
look, I guess that it's only good on textures that are more or less
scale invariant.



Jean-Francois Panisset (panisset@mcrcim.mcgill.ca) writes:

You will probably be disappointed to learn that in many cases, it is done by
hand using a paint-like program.  Basically, you just cut and paste bits and
pieces from the edges and use air-brushing functions until the tiled image
looks "good enough".  You would be surprised at how good the results of this
procedure can be.  I have tried a paint program on the SGI called Artisan
(from Media Logic).  It has a function where you set a source point, a
destination point and it then copies pixels around the source point to the
region around the destination point.  Thus if you have an aerial photograph
and you need to add a forest region on the boundary of the image, just set the
source point in the middle of an already existing forest in your image and you
can "draw" the required forest.  Of course, some artistic skill is required to
make the whole thing look really good (which I do not claim to possess...).



Rich Gossweiler <rg3h@jade.cs.Virginia.EDU> writes:

I saw your posting and am interested in the solution to seamless texture
tiling (STT) as well.  My solution was to warp the image -- which was adequate
for "wood" and some forms of greenery (ruffage).  In general though, this was
lousy.

I'd be inclined to change the left and right edges to match in color by
picking two points along the same row, somewhat in from the edge.  Then I'd
interpolate the edge colors from the line the two picked points create in a
color-cube space.  For the top and bottom edges, I'd use Euclidean distances
and change their values with respect to the distance from the corners.  Having
no image processing experience, and having never tried this, this probably
won't work, but I did not want to overlook it.


John Spackman <john@lightwork.co.uk> writes:

If you tessellate by reflecting at ech tile boundary, you get some free
continuity ....

[a few people mentioned this one, which I should have mentioned as something
I've done and found lacking.  If you ever need to reference it, here's where I
first saw it:

%A Richard J. Carey
%A Donald P. Greenberg
%T Textures for Realistic Image Synthesis
%J Computers and Graphics
%V 9
%N 2
%D 1985
%P 125-138

Actually, Rikk just showed it to me, but there's the reference. -- Eric]


Haakan Andersson (zap@lysator.liu.se) writes:

To obtain seamless textures, I think I know of three methods that comes to
mind immediately.

* The mirror trick 

  Simply mirror you original texture in X and Y. You will get something
  that repeats back 'n forth, but it is seamless. The problem is that this
  tiling is often visible, and that the seam may still be visible due to
  the sharp bend of for instance wood veins in the flip-seam.

  HOWEVER: If you have an image manipulation package that allows you to
  'stretch' around the images a bit, using the flipped images per above and
  then fooling around with the center (without messing with the edges) may
  give a decent-ish result.

* Crossfading

  Assume you have a square scanned piece of wood. Let's give it a 
  0,0 - 1,1 coordinate range. 

  For simplicity, let us only discuss the X coordinate case.

  (Sadly, we only get a 0,5x0,5 piece of patternmatched wood out of a 1x1
  piece of original wood if one uses this exact setup, but...)

  At the left edge of the output image, do NOT place the left edge of the 
  scanned wood! Instead the middle (X = 0.5 in input wood) are used, so we
  go from (left of output) = (0.5X of input) and (right of output) = (1.0 X
  of input). However, we cross-dissolve this with another image, who has
  (left of output = 0.0 X of input) and (right of output) = (0.5 X of input).

  This gives us the exact 0.5 condition at the edges, with a smooth cross
  dissolve over the actual texture. Works quite OK for wood.

* Do it by hand

  In your favourite paint program, do something like any or both of the above.
  I recommend using a variety of the second trick. The problem is often that
  you try (often in vain) to make the EDGES match. Stop that. Shift the image
  by half it's size in X and Y (assuming wraparound), thus making the EDGES 
  THEMSELVES automagically matched. Then perform all fixing-up in the 
  'interior' of the image with stretching, overpainting, cut-n-paste,
  dissolving and all that jazz.

I myself use all three with increasingly good results going from the
first idea listed here to the last. Of course, an increasing workload is
imposed for each step down the list too :-)

The best result is the handmade one, even if the cross-dissolved can look 
good too. However, it can appear a bit blurrish in the 50%-50% regions, and
they can sometimes show as "blurred crosses" on the surfaces.

Perhaps an ideal one would be the cross dissolve with some kind of triangu-
lation morph of the image so one could assemble them to look good-ish near
the middle so the crossdissolve won't look blurred.

Just my 0.02 swedish crowns (speaking of wich we are just this authum stopping
the use of the 0.10 swedish crown coin, meaning that 0.02 swedish crowns is,
indeed, worthless)


[I asked what he meant by cross-dissolve...]

Yup. Cross-dissolve was perhaps a bad choice of wording: Cross-fade
or whatever is perhaps a better term. But you get it. And
then you do the same for Y. Ok, you "lose" 3/4 of your original texture
this way, but you get a patternmatched one in return. It works good for
things like wood, stone and marble, and not-so-good for things like
brick, wallpaper and newspaper :-)

It can also be fully automated ;-)

You can also "expand" the idea to shrink the cross-fade area to one
quarter of the image (or less depending on your subject matter)
E.g. image overlaps like this:


   0.5---------------------------1.0
                       0.0--------------------------0.5
                       <X-fade-here>


Similar in the Y axis. However, this can generate visible artifacts perhaps.

I've never tried this last thing it just popped into my mind. Like most
things do ;-)


John M. Fujii <fujii@hpfujii.fc.hp.com> writes:

Here's my favorite recipe for disaster:

1) Scan image of image you want to repeat (such as wood grain, bricks, etc...)

2) In image processing software like Adobe Photoshop(tm), examine for
   opportunities to create the boundary wraps.  (The mortar in brick images,
   for example.)

3) Since the edges are the areas of interest, you want to do your blending
   slightly "in" from each edge.  Since the abutment has to happen explicitly
   at the edges, trim the image at some point "in" from the edge.  Copy this
   break to the opposite side of the texture.  The trick will be to blend the
   overlapping area with what is already below it (artistic license comes to
   play here).  The edges you created from the cut, however, will align
   exactly.  Effectively, you are forcing the wrap with information you
   already have in the map (but you are also losing some information along the
   way by reducing it with your cuts, so anticipate this factor when you
   scan).

	a)      +--------------+        Original Map
		|xxyzzzzzzzzbaa|
		|xxxyyyyzzzzzbb|
		|xxxxxyyyzzzbbb|
		+--------------+

			    8<
	b)      +----------  |  ----+   Trim it and move it
		|xxyzzzzzzz  |  zbaa|
		|xxxyyyyzzz  |  zzbb|
		|xxxxxyyyzz  |  zbbb|
		+----------  |  ----+

	c)         +----  ----------+   Blend it
		   |zbbx  xxyzzzzzzz|
		   |zzxx  xxxyyyyzzz|
		   |zbxx  xxxxxyyyzz|
		   +----  ----------+

	d)      Repeat for other edges

Yeah, it's a hack and labor intensive, but if your image does not have too
much variation between opposite edges, you can usually work out the blending
issues with the cool tools retouching packages like Photoshop afford you.  (By
the way, you can easily check the wrapping effects in this package by copying
your tiles into another workspace...  wonders never cease!)

Maybe this was too obvious, but it works for me.  Abstracts work pretty well,
however people-images do not (unless you put in your own artificial
background...)

----

Someone whose post I lost recommended doing mirroring, essentially, but
using something like the function:

	g'(u,v) = g((sin(u)+1.0)/2.0,(sin(v)+1.0)/2.0)

where g(u,v) is the normal texture lookup function given u and v.  I may have
this wrong...  Basically, this converts the u,v's to a cyclical function which
stretches things at the mirror edge.  Haven't a clue if this does anything
good, but it was an interesting idea.