//////////////////////////////////////////////////////////////////////// // Lens Swamp //////////////////////////////////////////////////////////////////////// // GLOBAL VARIABLES //////////////////////////////////////////////////// KohonenMap TheMap; // The lens swamp map. KohonenMapView TheView; // The lens swamp view. String MapName; // The name of the map. int NrLenses; // Number of lenses to be mapped. Lens[] Lenses; // Lenses to be mapped. PImage BGImg; // Background image. // GLOBAL METHODS ////////////////////////////////////////////////////// // // Set up. // void setup() { // Load the background picture. loadBackground("background.jpg"); // Set a text font. setFont(); // Load the data file. loadDataFile("lens.dat"); // Create a Kohonen map. TheMap = new KohonenMap(Lenses); // Create a map view. TheView = new KohonenMapView(TheMap); } // // Draw. // void draw() { // Set the background image. background(BGImg); // Do a step of learning. TheMap.step(); // Show current map status. TheView.show(); // Show map name at bottom-right. colorMode(RGB, 255); textAlign(RIGHT); fill( 0); text(MapName, width - 1, height - 1); fill(255); text(MapName, width - 2, height - 2); // If the map has been learnt, stop calling draw(); if (TheMap.finished()) noLoop(); } // // Load the lens data file. // void loadDataFile(String filename) { // Load the lens data file. String[] lensData = loadStrings(filename); // The first line of the file is the map name. MapName = lensData[0]; // Create lens instances. NrLenses = lensData.length - 1; Lenses = new Lens[NrLenses]; for (int i = 1; i <= NrLenses; i++) { Lenses[i - 1] = new Lens(lensData[i]); } } // // Load the background picture. // void loadBackground(String filename) { BGImg = loadImage(filename); size(BGImg.width, BGImg.height); } // // Set a font for text. // void setFont() { PFont font = loadFont("Serif.plain-12.vlw"); textFont(font); } // CLASS DEFINITIONS /////////////////////////////////////////////////// // // The Kohonen (self-organizing) map class. // class KohonenMap { // CONSTRUCTORS ////////////////////////////////////////////////////// // // The sole constructor. // // @param targetVectors // Target vectors for learning. // KohonenMap(Vector[] targetVectors) { // Hold given target vectors. _targetVectors = targetVectors; // Reset the finish flag. _finished = false; // Create cells. _nrRowCells = 30; _nrColCells = 30; _nrCells = _nrRowCells * _nrColCells; _cells = new Cell[_nrCells]; for (int i = 0; i < _nrCells; i++) { _cells[i] = new Cell(); } // Set the iteration counter. _iteration = 0; _maxIteration = 30000; } // ACCESSORS ///////////////////////////////////////////////////////// boolean finished() { return _finished; } int getNrCells() { return _nrCells; } int getNrRowCells() { return _nrRowCells; } int getNrColCells() { return _nrColCells; } Cell getCell(int x, int y) { return _cells[x + y * _nrColCells]; } int getIteration() { return _iteration; } int getMaxIteration() { return _maxIteration; } int getNrTargetVectors() { return _targetVectors.length; } Vector getTargetVector(int i) { return _targetVectors[i]; } // PUBLIC METHODS //////////////////////////////////////////////////// // // Do a step of learning. // void step() { for (int i = 0; i < 200; i++) { int t = constrain(int(random(getNrTargetVectors())), 0, getNrTargetVectors() - 1); updateNeighbors(findBMUTo(t), t); _iteration++; } if (_iteration >= _maxIteration) _finished = true; } // // Find the best matching unit to given target. // int findBMUTo(int target) { Vector tv = _targetVectors[target]; float minD = tv.distance(_cells[0]); int bmu = 0; for (int i = 1; i < _nrCells; i++) { float d = tv.distance(_cells[i]); if (minD > d) { minD = d; bmu = i; } } return bmu; } // // Update neighbor cells. // void updateNeighbors(int ci, int ti) { // Get coordinates of given cell. int cx = ci % _nrColCells; int cy = ci / _nrColCells; // Get current neighbor radius. float r = neighborRadius(); // <<< For each row of cells >>> for (int y = 0; y < _nrRowCells; y++) { // <<< For each cells of the row >>> for (int x = 0; x < _nrColCells; x++) { // Update the cell. float d = dist(cx, cy, x, y); _cells[x + y * _nrColCells].moveTo(_targetVectors[ti], 0.8 * exp(-d / r)); } // >>> For each cells of the row <<< } // >>> For each row of cells <<< } // // Current neighbor radius. // float neighborRadius() { return (30.0 * (1.0 - (float) _iteration / _maxIteration)); } // DATA MEMBERS ////////////////////////////////////////////////////// boolean _finished; Vector[] _targetVectors; Cell[] _cells; int _nrCells; int _nrRowCells; int _nrColCells; int _iteration; int _maxIteration; } // // Map view class. // class KohonenMapView { static final int RIM = 50; KohonenMapView(KohonenMap map) { _map = map; // Width and height of the swamp body. _sw = width - RIM * 2; _sh = height - RIM * 2; // Number of Row/Column cells. _rcell = _map.getNrRowCells(); _ccell = _map.getNrColCells(); // Pixels per grid. _px = (float) _sw / (_ccell - 1); _py = (float) _sh / (_rcell - 1); } void show() { for (int y = 0; y < _sh; y++) { int iy = constrain((int) (y / _py), 0, _rcell - 1); float ry = (float) (y - iy * _py) / ((iy + 1) * _py - iy * _py); for (int x = 0; x < _sw; x++) { int ix = constrain((int) (x / _px), 0, _ccell - 1); float rx = (float) (x - ix * _px) / ((ix + 1) * _px - ix * _px); // Get the bilinear interpolations of price and posession. Cell c00 = _map.getCell(ix, iy); Cell c01 = _map.getCell(ix + 1, iy); Cell c10 = _map.getCell(ix, iy + 1); Cell c11 = _map.getCell(ix + 1, iy + 1); float price = bilinear(c00.getPrice(), c01.getPrice(), c10.getPrice(), c11.getPrice(), rx, ry); float possession = bilinear(c00.getPossession(), c01.getPossession(), c10.getPossession(), c11.getPossession(), rx, ry); // Set the color of the point. colorMode(HSB, 1.0); float h = (possession + 18.0)/ 27.0; price = constrain(log(price) / log(10) - 3.0, 0.0, 5.0) / 5.0; float s = price; float b = 1.0 - price * 0.9; stroke(h, s, b); // Draw the point. point(x + RIM, y + RIM); } } for (int t = 0; t < _map.getNrTargetVectors(); t++) { int bmu = _map.findBMUTo(t); int x = (int) (RIM + (bmu % _map.getNrColCells()) * _px); int y = (int) (RIM + (bmu / _map.getNrColCells()) * _py); colorMode(RGB, 255); stroke(0); fill(255); rectMode(CENTER); rect(x, y, 3, 3); textAlign(CENTER); String name = ((Lens) _map.getTargetVector(t)).getName(); text(name, x, y - 3); } String msg = "Learning... " + nfc(_map.getIteration()) + "/" + nfc(_map.getMaxIteration()); colorMode(RGB, 255); textAlign(LEFT); fill( 0); text(msg, 2, height - 1); fill(255); text(msg, 1, height - 2); } // // Bilinear interpolation. // // x00--------r01----------x01 // | | | // | | | // r10--------ret | // | | // | | // x10---------------------x11 // // 0.0 <= r01, r10 <= 1.0 // float bilinear(float x00, float x01, float x10, float x11, float r01, float r10) { return lerp(lerp(x00, x01, r01), lerp(x10, x11, r01), r10); } KohonenMap _map; int _sw; int _sh; int _rcell; int _ccell; float _px; float _py; } abstract class Vector { // CONSTANT VALUES /////////////////////////////////////////////////// static final int NR_ELEMENTS = 6; // CONSTRUCTORS ////////////////////////////////////////////////////// Vector() { _elements = new float[NR_ELEMENTS]; } // ACCESSORS ///////////////////////////////////////////////////////// float getFocalLengthWide() { return _elements[0]; } float getFocalLengthTele() { return _elements[1]; } float getMaxApertureWide() { return _elements[2]; } float getMaxApertureTele() { return _elements[3]; } float getPrice() { return _elements[4]; } float getPossession() { return _elements[5]; } // // Euclidean distance from another Vector. // float distance(Vector p) { float sum = 0.0; for (int i = 0; i < NR_ELEMENTS; i++) { sum += sq(p._elements[i] - _elements[i]); } return sqrt(sum); } void moveTo(Vector t, float rate) { for (int i = 0; i < NR_ELEMENTS; i++) { _elements[i] += rate * (t._elements[i] - _elements[i]); } } float[] _elements; } // // Lens class. // class Lens extends Vector { // CONSTRUCTORS ////////////////////////////////////////////////////// // // The sole constructor. // // @param data // A Lens data line from the data file. // Lens(String data) { // Extract the name of the lens. // It is assumed that the name is the first field of the data line // embraced by double quotations. int dq1 = data.indexOf('"'); int dq2 = data.indexOf('"', dq1 + 1); _name = data.substring(dq1 + 1, dq2); // Extract following six lens parameters seperated by commas; // focal length (W), // focal length (T), // maximum aperture (W), // maximum aperture (T), // price, // and possession. _elements = float(split(data.substring(data.indexOf(',', dq2) + 1), ',')); } String getName() { return _name; } // DATA MEMBERS ////////////////////////////////////////////////////// String _name; } class Cell extends Vector { // CONSTRUCTORS ////////////////////////////////////////////////////// Cell() { for (int i = 0; i < Vector.NR_ELEMENTS; i++) { _elements[i] = random(10000000.0); } } }