Changeset 2031


Ignore:
Timestamp:
Nov 20, 2007, 12:04:32 AM (13 years ago)
Author:
Sam Hocevar
Message:
  • More about photomosaics.
Location:
www/study
Files:
3 added
2 edited
2 moved

Legend:

Unmodified
Added
Removed
  • www/study/part6.html

    r2030 r2031  
    4040of a bigger image. </p>
    4141
    42 <p> Since we don’t have many images at our disposition, we will simply
    43 cut Lenna into small chunks and use these parts to create mosaics. This is
    44 our <b>image database</b>: </p>
     42<p> Since we don’t have many images at our disposal, we will simply cut Lenna
     43into small chunks (called <b>tiles</b>) and use these parts to create mosaics.
     44This is our <b>tile database</b>: </p>
    4545
    4646<p style="text-align: center;">
     
    4949</p>
    5050
    51 <p> Here is an example of a simple photographic mosaic method where the
    52 original picture is cut into smaller chunks and each chunk is matched to
    53 the most resembling item in the image database: </p>
     51<p> Generating a photomosaic consists in subdividing the original picture
     52into <i>x</i> rectangular cells and find <i>x</i> tiles in the database
     53(with or without duplicates, depending on the set of rules that is decided)
     54so that when recombined the resulting image resembles the original picture.
     55By the way, this technique is covered by Runaway Technology Inc’s
     56<a href="http://www.freepatentsonline.com/6137498.html">U.S. patent
     576137498</a>. </p>
     58
     59<p> Picking the right tile for the right cell in the grid is a very
     60expensive and complicated operation. One of the biggest problems is the
     61cost of a database lookup: comparing each tile area pixel-by-pixel
     62is an O(N) operation where N is the size of the database. We can resort
     63to <b>image classification</b> in order to speed up database lookups. </p>
     64
     65<h3> 6.1. Image classification </h3>
     66
     67<p> One of the simplest image classification technique is the storage of
     68each tile’s <b>average colour</b> into a separate database that is used for
     69best match lookups: </p>
    5470
    5571<p style="text-align: center;">
    56   <img src="out6-0-2.png" width="80" height="80"
     72  <img src="out6-1-1.png" width="168" height="120"
     73       class="inline" alt="1 feature extracted from Lenna patterns" />
     74</p>
     75
     76<p> When creating the mosaic, we then only need to check the average colour
     77instead of comparing each pixel one by one. Below is the result of the
     78technique applied on a portion of the Lenna picture: </p>
     79
     80<p style="text-align: center;">
     81  <img src="out6-1-2.png" width="80" height="80"
    5782       class="inlinetop" alt="Lenna (detail)" />
    58   <img src="out6-0-3.png" width="416" height="416"
     83  <img src="out6-1-3.png" width="416" height="416"
     84       class="inline" alt="Mosaic created from Lenna’s detail" />
     85</p>
     86
     87<p> Better results can be achieved by storing <b>four colour values</b>, one
     88for each corner of the tile: </p>
     89
     90<p style="text-align: center;">
     91  <img src="out6-1-4.png" width="248" height="176"
     92       class="inline" alt="4 features extracted from Lenna patterns" />
     93</p>
     94
     95<p> Having 12 values (4 RGB triplets) is still a lot less than the original
     96value of 3072 (for 32×32 tiles), and the results show clear improvement,
     97for instance the feather-hair frontier is now a lot smoother: </p>
     98
     99<p style="text-align: center;">
     100  <img src="out6-1-2.png" width="80" height="80"
     101       class="inlinetop" alt="Lenna (detail)" />
     102  <img src="out6-1-5.png" width="416" height="416"
    59103       class="inline" alt="Mosaic created from Lenna’s detail" />
    60104</p>
  • www/study/study.py

    r2030 r2031  
    3939        c = self.colorResolve((r, g, b))
    4040        self.setPixel((x, y), c)
     41    def getCrop(self, x, y, w, h):
     42        dest = Image((w, h), True)
     43        self.copyTo(dest, (-x, -y))
     44        return dest
    4145
    4246# Manipulate gamma values
     
    838842    return coeffs
    839843
    840 def test603(src, sqw, sqh, tnlist, coeffs, dx, dy):
    841     (w, h) = src.size()
     844def test601(tnlist, cols):
    842845    (tnw, tnh) = tnlist[0].size()
     846    dw = cols
     847    dh = (len(tnlist) + cols - 1) / cols
     848    dest = Image((dw * tnw + 8 * (dw + 1), dh * tnh + 8 * (dh + 1)), True)
     849    for (n, img) in enumerate(tnlist):
     850        di = 8 + (n % dw) * (tnw + 8)
     851        dj = 8 + (n / dw) * (tnh + 8)
     852        for y in range(tnh):
     853            for x in range(tnw):
     854                (r, g, b) = img.getRgb(x, y)
     855                dest.setRgb(di + x, dj + y, r, g, b)
     856    return dest
     857
     858if chapter(6):
     859    tnlist = mosaic_split(lenna256, 32, 32)
     860    test601(tnlist, 10).save("out6-0-1.png")
     861
     862# Output 6.1.1: extract 1 colour feature from mosaic tiles
     863# Output 6.1.2: crop Lenna
     864# Output 6.1.3: generate a mosaic from the 1-feature database
     865# Output 6.1.4: extract 4 colour features from mosaic tiles
     866# Output 6.1.5: generate a mosaic from the 4-feature database
     867def test61x(coeffs, cols, tnw, tnh):
     868    dx = len(coeffs[0][0])
     869    dy = len(coeffs[0])
     870    dw = cols
     871    dh = (len(coeffs) + cols - 1) / cols
     872    dest = Image((dw * tnw + 8 * (dw + 1), dh * tnh + 8 * (dh + 1)), True)
     873    for (n, tab) in enumerate(coeffs):
     874        di = 8 + (n % dw) * (tnw + 8)
     875        dj = 8 + (n / dw) * (tnh + 8)
     876        for y in range(tnh):
     877            for x in range(tnw):
     878                (r, g, b) = tab[y * dy / tnh][x * dx / tnw]
     879                dest.setRgb(di + x, dj + y, Gamma.ItoC(r), Gamma.ItoC(g), Gamma.ItoC(b))
     880    return dest
     881
     882def test61y(src, sqw, sqh, tnlist, coeffs):
     883    (w, h) = src.size()
     884    (tnw, tnh) = tnlist[0].size()
     885    dx = len(coeffs[0][0])
     886    dy = len(coeffs[0])
    843887    nx = w / sqw
    844888    ny = h / sqh
     
    873917    return dest
    874918
    875 def test602(src, x, y, w, h):
    876     dest = Image((w, h), True)
    877     src.copyTo(dest, (-x, -y))
    878     return dest
    879 
    880 def test601(tnlist, cols):
    881     (tnw, tnh) = tnlist[0].size()
    882     dw = cols
    883     dh = (len(tnlist) + cols - 1) / cols
    884     dest = Image((dw * tnw + 8 * (dw + 1), dh * tnh + 8 * (dh + 1)), True)
    885     for (n, img) in enumerate(tnlist):
    886         di = 8 + (n % dw) * (tnw + 8)
    887         dj = 8 + (n / dw) * (tnh + 8)
    888         for y in range(tnh):
    889             for x in range(tnw):
    890                 (r, g, b) = img.getRgb(x, y)
    891                 dest.setRgb(di + x, dj + y, r, g, b)
    892     return dest
    893 
    894919if chapter(6):
    895     tnlist = mosaic_split(lenna256, 32, 32)
    896     coeffs = mosaic_analyse(tnlist, 2, 2)
    897     test601(tnlist, 10).save("out6-0-1.png")
    898     out602 = test602(lenna256, 100, 90, 80, 80)
    899     out602.save("out6-0-2.png")
    900     test603(out602, 6, 6, tnlist, coeffs, 2, 2).save("out6-0-3.png")
     920    coeffs1x1 = mosaic_analyse(tnlist, 1, 1)
     921    test61x(coeffs1x1, 10, 8, 8).save("out6-1-1.png")
     922    out612 = lenna256.getCrop(100, 90, 80, 80)
     923    out612.save("out6-1-2.png")
     924    test61y(out612, 6, 6, tnlist, coeffs1x1).save("out6-1-3.png")
     925
     926    coeffs2x2 = mosaic_analyse(tnlist, 2, 2)
     927    test61x(coeffs2x2, 10, 16, 16).save("out6-1-4.png")
     928    test61y(out612, 6, 6, tnlist, coeffs2x2).save("out6-1-5.png")
    901929
    902930##############################################################################
Note: See TracChangeset for help on using the changeset viewer.