From a1d976fc30fb3ec6fdadf9c6e42c3ed6da8c0dda Mon Sep 17 00:00:00 2001 From: Gene Kogan Date: Thu, 28 Jan 2016 19:59:21 +0530 Subject: [PATCH] first commit --- README.md | 4 +- download_images.py | 26 + example-images/Makefile | 13 + example-images/Project.xcconfig | 17 + example-images/addons.make | 3 + example-images/bin/data/.gitkeep | 0 example-images/config.make | 142 ++ .../example-images.xcodeproj/project.pbxproj | 1372 +++++++++++++++++ .../UserInterfaceState.xcuserstate | Bin 0 -> 16063 bytes .../xcschemes/example-images Debug.xcscheme | 86 ++ .../xcschemes/example-images Release.xcscheme | 86 ++ .../xcschemes/xcschememanagement.plist | 14 + example-images/openFrameworks-Info.plist | 22 + example-images/src/main.cpp | 13 + example-images/src/ofApp.cpp | 131 ++ example-images/src/ofApp.h | 37 + example/Makefile | 13 + example/Project.xcconfig | 17 + example/addons.make | 1 + example/config.make | 142 ++ example/example.xcodeproj/project.pbxproj | 837 ++++++++++ .../UserInterfaceState.xcuserstate | Bin 0 -> 20038 bytes .../xcschemes/example Debug.xcscheme | 86 ++ .../xcschemes/example Release.xcscheme | 86 ++ .../xcschemes/xcschememanagement.plist | 14 + example/openFrameworks-Info.plist | 22 + example/src/main.cpp | 13 + example/src/ofApp.cpp | 191 +++ example/src/ofApp.h | 34 + ofxaddons_thumbnail.png | Bin 0 -> 49891 bytes setup_ccv.sh | 14 + src/bhtsne/sptree.cpp | 428 +++++ src/bhtsne/sptree.h | 115 ++ src/bhtsne/tsne.cpp | 758 +++++++++ src/bhtsne/tsne.h | 63 + src/bhtsne/vptree.h | 272 ++++ src/ofxTSNE.cpp | 69 + src/ofxTSNE.h | 13 + 38 files changed, 5152 insertions(+), 2 deletions(-) create mode 100644 download_images.py create mode 100644 example-images/Makefile create mode 100644 example-images/Project.xcconfig create mode 100644 example-images/addons.make create mode 100644 example-images/bin/data/.gitkeep create mode 100644 example-images/config.make create mode 100644 example-images/example-images.xcodeproj/project.pbxproj create mode 100644 example-images/example-images.xcodeproj/project.xcworkspace/xcuserdata/gene.xcuserdatad/UserInterfaceState.xcuserstate create mode 100644 example-images/example-images.xcodeproj/xcshareddata/xcschemes/example-images Debug.xcscheme create mode 100644 example-images/example-images.xcodeproj/xcshareddata/xcschemes/example-images Release.xcscheme create mode 100644 example-images/example-images.xcodeproj/xcuserdata/gene.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 example-images/openFrameworks-Info.plist create mode 100644 example-images/src/main.cpp create mode 100644 example-images/src/ofApp.cpp create mode 100644 example-images/src/ofApp.h create mode 100644 example/Makefile create mode 100644 example/Project.xcconfig create mode 100644 example/addons.make create mode 100644 example/config.make create mode 100644 example/example.xcodeproj/project.pbxproj create mode 100644 example/example.xcodeproj/project.xcworkspace/xcuserdata/gene.xcuserdatad/UserInterfaceState.xcuserstate create mode 100644 example/example.xcodeproj/xcshareddata/xcschemes/example Debug.xcscheme create mode 100644 example/example.xcodeproj/xcshareddata/xcschemes/example Release.xcscheme create mode 100644 example/example.xcodeproj/xcuserdata/gene.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 example/openFrameworks-Info.plist create mode 100644 example/src/main.cpp create mode 100644 example/src/ofApp.cpp create mode 100644 example/src/ofApp.h create mode 100644 ofxaddons_thumbnail.png create mode 100755 setup_ccv.sh create mode 100755 src/bhtsne/sptree.cpp create mode 100755 src/bhtsne/sptree.h create mode 100755 src/bhtsne/tsne.cpp create mode 100755 src/bhtsne/tsne.h create mode 100755 src/bhtsne/vptree.h create mode 100644 src/ofxTSNE.cpp create mode 100644 src/ofxTSNE.h diff --git a/README.md b/README.md index 92ce5da..12fb9b9 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -## ofxTSNE +# ofxTSNE ofxTSNE is an [addon](https://www.ofxaddons.com) for [openframeworks](https://www.openframeworks.cc) which wraps the [t-SNE](https://lvdmaaten.github.io/tsne/) (t-Distributed Stochastic Neighbor Embedding) algorithm by [Laurens van der Maaten](https://lvdmaaten.github.io). @@ -19,7 +19,7 @@ ofxTSNE is very simple to run, containing only one function. The harder part is ![t-SNE images from Caltech-256](http://www.genekogan.com/images/misc/ofxTsne2.jpg) -`example-images` applies t-SNE to a directory of images. It uses [ofxCcv](https://www.github.com/kylemcdonald/ofxCcv) to encode each image as a compact (4096-dim) feature vector derived from a convolutional neural net trained on ImageNet. The resulting representation captures high-level similarities among images, enabling ofxTSNE to group them effectively according more to content (e.g. images of cats get clustered together), relatively invariant to changes in color, lighting, position, etc. +`example-images` applies t-SNE to a directory of images. It uses [ofxCcv](https://www.github.com/kylemcdonald/ofxCcv) to encode each image as a compact (4096-dim) feature vector derived from a trained convolutional neural network. The resulting representation captures high-level similarities among images, enabling ofxTSNE to group them effectively according more to content (e.g. images of cats get clustered together), relatively invariant to changes in color, lighting, position, etc. To run this example, you need to take a few extra steps. diff --git a/download_images.py b/download_images.py new file mode 100644 index 0000000..ad155e7 --- /dev/null +++ b/download_images.py @@ -0,0 +1,26 @@ +import urllib +import os + + +root_dir = 'example-images/bin/data/images' +num_images_per_category = 30 +categories = [ [2, "american-flag"], [63, "electric-guitar-101"], [56, "dog"], [24, "butterfly"], [15, "bonsai"], [25, "cactus"], [38, "chimp"], [60, "duck"], [53, "desk-globe"], [95, "hamburger"], [195, "soda-can"], [187, "skyscraper"], [150, "octopus"], [152, "owl"], [136, "mandolin"], [127, "laptop-101"], [80, "frog"], [29, "cannon"], [37, "chess-board"], [10, "beer-mug"], [246, "wine-bottle"], [250, "zebra"], [221, "tomato"], [113, "hummingbird"], [91, "grand-piano-101"], [87, "goldfish"], [86, "golden-gate-bridge"], [81, "frying-pan"], [57, "dolphin-101"], [46, "computer-monitor"], [22, "buddha-101"] ] + + + + +cmd = 'mkdir %s' % root_dir +os.system(cmd) + +url_root = 'http://www.vision.caltech.edu/Image_Datasets/Caltech256/images/' +for c in categories: + for i in range(21,31):#range(1,num_images_per_category+1): + path = 'http://www.vision.caltech.edu/Image_Datasets/Caltech256/images/%03d.%s/%03d_%04d.jpg' % (c[0], c[1], c[0], i) + print "download %s"%path + urllib.urlretrieve(path, "%s/%s-%04d.jpg " % (root_dir, c[1], i)) + + + + + + diff --git a/example-images/Makefile b/example-images/Makefile new file mode 100644 index 0000000..8d8e4c0 --- /dev/null +++ b/example-images/Makefile @@ -0,0 +1,13 @@ +# Attempt to load a config.make file. +# If none is found, project defaults in config.project.make will be used. +ifneq ($(wildcard config.make),) + include config.make +endif + +# make sure the the OF_ROOT location is defined +ifndef OF_ROOT + OF_ROOT=$(realpath ../../..) +endif + +# call the project makefile! +include $(OF_ROOT)/libs/openFrameworksCompiled/project/makefileCommon/compile.project.mk diff --git a/example-images/Project.xcconfig b/example-images/Project.xcconfig new file mode 100644 index 0000000..e570b15 --- /dev/null +++ b/example-images/Project.xcconfig @@ -0,0 +1,17 @@ +//THE PATH TO THE ROOT OF OUR OF PATH RELATIVE TO THIS PROJECT. +//THIS NEEDS TO BE DEFINED BEFORE CoreOF.xcconfig IS INCLUDED +OF_PATH = ../../.. + +//THIS HAS ALL THE HEADER AND LIBS FOR OF CORE +#include "../../../libs/openFrameworksCompiled/project/osx/CoreOF.xcconfig" + +//ICONS - NEW IN 0072 +ICON_NAME_DEBUG = icon-debug.icns +ICON_NAME_RELEASE = icon.icns +ICON_FILE_PATH = $(OF_PATH)/libs/openFrameworksCompiled/project/osx/ + +//IF YOU WANT AN APP TO HAVE A CUSTOM ICON - PUT THEM IN YOUR DATA FOLDER AND CHANGE ICON_FILE_PATH to: +//ICON_FILE_PATH = bin/data/ + +OTHER_LDFLAGS = $(OF_CORE_LIBS) $(OF_CORE_FRAMEWORKS) +HEADER_SEARCH_PATHS = $(OF_CORE_HEADERS) diff --git a/example-images/addons.make b/example-images/addons.make new file mode 100644 index 0000000..f583b9c --- /dev/null +++ b/example-images/addons.make @@ -0,0 +1,3 @@ +ofxCcv +ofxGui +ofxTSNE diff --git a/example-images/bin/data/.gitkeep b/example-images/bin/data/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/example-images/config.make b/example-images/config.make new file mode 100644 index 0000000..df10f64 --- /dev/null +++ b/example-images/config.make @@ -0,0 +1,142 @@ +################################################################################ +# CONFIGURE PROJECT MAKEFILE (optional) +# This file is where we make project specific configurations. +################################################################################ + +################################################################################ +# OF ROOT +# The location of your root openFrameworks installation +# (default) OF_ROOT = ../../.. +################################################################################ +# OF_ROOT = ../../.. + +################################################################################ +# PROJECT ROOT +# The location of the project - a starting place for searching for files +# (default) PROJECT_ROOT = . (this directory) +# +################################################################################ +# PROJECT_ROOT = . + +################################################################################ +# PROJECT SPECIFIC CHECKS +# This is a project defined section to create internal makefile flags to +# conditionally enable or disable the addition of various features within +# this makefile. For instance, if you want to make changes based on whether +# GTK is installed, one might test that here and create a variable to check. +################################################################################ +# None + +################################################################################ +# PROJECT EXTERNAL SOURCE PATHS +# These are fully qualified paths that are not within the PROJECT_ROOT folder. +# Like source folders in the PROJECT_ROOT, these paths are subject to +# exlclusion via the PROJECT_EXLCUSIONS list. +# +# (default) PROJECT_EXTERNAL_SOURCE_PATHS = (blank) +# +# Note: Leave a leading space when adding list items with the += operator +################################################################################ +# PROJECT_EXTERNAL_SOURCE_PATHS = + +################################################################################ +# PROJECT EXCLUSIONS +# These makefiles assume that all folders in your current project directory +# and any listed in the PROJECT_EXTERNAL_SOURCH_PATHS are are valid locations +# to look for source code. The any folders or files that match any of the +# items in the PROJECT_EXCLUSIONS list below will be ignored. +# +# Each item in the PROJECT_EXCLUSIONS list will be treated as a complete +# string unless teh user adds a wildcard (%) operator to match subdirectories. +# GNU make only allows one wildcard for matching. The second wildcard (%) is +# treated literally. +# +# (default) PROJECT_EXCLUSIONS = (blank) +# +# Will automatically exclude the following: +# +# $(PROJECT_ROOT)/bin% +# $(PROJECT_ROOT)/obj% +# $(PROJECT_ROOT)/%.xcodeproj +# +# Note: Leave a leading space when adding list items with the += operator +################################################################################ +# PROJECT_EXCLUSIONS = + +################################################################################ +# PROJECT LINKER FLAGS +# These flags will be sent to the linker when compiling the executable. +# +# (default) PROJECT_LDFLAGS = -Wl,-rpath=./libs +# +# Note: Leave a leading space when adding list items with the += operator +################################################################################ + +# Currently, shared libraries that are needed are copied to the +# $(PROJECT_ROOT)/bin/libs directory. The following LDFLAGS tell the linker to +# add a runtime path to search for those shared libraries, since they aren't +# incorporated directly into the final executable application binary. +# TODO: should this be a default setting? +# PROJECT_LDFLAGS=-Wl,-rpath=./libs + +################################################################################ +# PROJECT DEFINES +# Create a space-delimited list of DEFINES. The list will be converted into +# CFLAGS with the "-D" flag later in the makefile. +# +# (default) PROJECT_DEFINES = (blank) +# +# Note: Leave a leading space when adding list items with the += operator +################################################################################ +# PROJECT_DEFINES = + +################################################################################ +# PROJECT CFLAGS +# This is a list of fully qualified CFLAGS required when compiling for this +# project. These CFLAGS will be used IN ADDITION TO the PLATFORM_CFLAGS +# defined in your platform specific core configuration files. These flags are +# presented to the compiler BEFORE the PROJECT_OPTIMIZATION_CFLAGS below. +# +# (default) PROJECT_CFLAGS = (blank) +# +# Note: Before adding PROJECT_CFLAGS, note that the PLATFORM_CFLAGS defined in +# your platform specific configuration file will be applied by default and +# further flags here may not be needed. +# +# Note: Leave a leading space when adding list items with the += operator +################################################################################ +# PROJECT_CFLAGS = + +################################################################################ +# PROJECT OPTIMIZATION CFLAGS +# These are lists of CFLAGS that are target-specific. While any flags could +# be conditionally added, they are usually limited to optimization flags. +# These flags are added BEFORE the PROJECT_CFLAGS. +# +# PROJECT_OPTIMIZATION_CFLAGS_RELEASE flags are only applied to RELEASE targets. +# +# (default) PROJECT_OPTIMIZATION_CFLAGS_RELEASE = (blank) +# +# PROJECT_OPTIMIZATION_CFLAGS_DEBUG flags are only applied to DEBUG targets. +# +# (default) PROJECT_OPTIMIZATION_CFLAGS_DEBUG = (blank) +# +# Note: Before adding PROJECT_OPTIMIZATION_CFLAGS, please note that the +# PLATFORM_OPTIMIZATION_CFLAGS defined in your platform specific configuration +# file will be applied by default and further optimization flags here may not +# be needed. +# +# Note: Leave a leading space when adding list items with the += operator +################################################################################ +# PROJECT_OPTIMIZATION_CFLAGS_RELEASE = +# PROJECT_OPTIMIZATION_CFLAGS_DEBUG = + +################################################################################ +# PROJECT COMPILERS +# Custom compilers can be set for CC and CXX +# (default) PROJECT_CXX = (blank) +# (default) PROJECT_CC = (blank) +# Note: Leave a leading space when adding list items with the += operator +################################################################################ +# PROJECT_CXX = +# PROJECT_CC = diff --git a/example-images/example-images.xcodeproj/project.pbxproj b/example-images/example-images.xcodeproj/project.pbxproj new file mode 100644 index 0000000..ab87279 --- /dev/null +++ b/example-images/example-images.xcodeproj/project.pbxproj @@ -0,0 +1,1372 @@ + + + + archiveVersion + 1 + classes + + objectVersion + 46 + objects + + A18A04F43F1A3D8641B0D261 + + explicitFileType + sourcecode.c.h + fileEncoding + 30 + isa + PBXFileReference + name + ofxTSNE.h + path + ../../../addons/ofxTSNE/src/ofxTSNE.h + sourceTree + SOURCE_ROOT + + 51A4134BF889D553064DCD2E + + fileRef + D985BBD882BD1582C610B30C + isa + PBXBuildFile + + D985BBD882BD1582C610B30C + + explicitFileType + sourcecode.cpp.cpp + fileEncoding + 30 + isa + PBXFileReference + name + ofxTSNE.cpp + path + ../../../addons/ofxTSNE/src/ofxTSNE.cpp + sourceTree + SOURCE_ROOT + + 1F7816F26F468BAC49B9ADD0 + + explicitFileType + sourcecode.c.h + fileEncoding + 30 + isa + PBXFileReference + name + vptree.h + path + ../../../addons/ofxTSNE/src/bhtsne/vptree.h + sourceTree + SOURCE_ROOT + + 54DCDCE01CF02EC24329509D + + explicitFileType + sourcecode.c.h + fileEncoding + 30 + isa + PBXFileReference + name + tsne.h + path + ../../../addons/ofxTSNE/src/bhtsne/tsne.h + sourceTree + SOURCE_ROOT + + BF438D1989EA8474A24FC372 + + fileRef + C5EC22EBF07D1C6136CAFF40 + isa + PBXBuildFile + + C5EC22EBF07D1C6136CAFF40 + + explicitFileType + sourcecode.cpp.cpp + fileEncoding + 30 + isa + PBXFileReference + name + tsne.cpp + path + ../../../addons/ofxTSNE/src/bhtsne/tsne.cpp + sourceTree + SOURCE_ROOT + + D361105E69E9248C8CDD4024 + + explicitFileType + sourcecode.c.h + fileEncoding + 30 + isa + PBXFileReference + name + sptree.h + path + ../../../addons/ofxTSNE/src/bhtsne/sptree.h + sourceTree + SOURCE_ROOT + + C289F16CCC88C08ABEBC3CE6 + + children + + 29CFCFEEAF44F7849B7A8E2C + D361105E69E9248C8CDD4024 + C5EC22EBF07D1C6136CAFF40 + 54DCDCE01CF02EC24329509D + 1F7816F26F468BAC49B9ADD0 + + isa + PBXGroup + name + bhtsne + sourceTree + <group> + + 84110FEC26026E23FCEDE55A + + children + + C289F16CCC88C08ABEBC3CE6 + D985BBD882BD1582C610B30C + A18A04F43F1A3D8641B0D261 + + isa + PBXGroup + name + src + sourceTree + <group> + + A2C3A140678F172C21702046 + + children + + 84110FEC26026E23FCEDE55A + + isa + PBXGroup + name + ofxTSNE + sourceTree + <group> + + 3F59654CA5CEEFA57C8D09DC + + fileRef + 29CFCFEEAF44F7849B7A8E2C + isa + PBXBuildFile + + 29CFCFEEAF44F7849B7A8E2C + + explicitFileType + sourcecode.cpp.cpp + fileEncoding + 30 + isa + PBXFileReference + name + sptree.cpp + path + ../../../addons/ofxTSNE/src/bhtsne/sptree.cpp + sourceTree + SOURCE_ROOT + + 0A1DAC09F322AE313A40706D + + explicitFileType + sourcecode.c.h + fileEncoding + 30 + isa + PBXFileReference + name + ofxToggle.h + path + ../../../addons/ofxGui/src/ofxToggle.h + sourceTree + SOURCE_ROOT + + 1CD33E884D9E3358252E82A1 + + fileRef + 907C5B5E104864A2D3A25745 + isa + PBXBuildFile + + 907C5B5E104864A2D3A25745 + + explicitFileType + sourcecode.cpp.cpp + fileEncoding + 30 + isa + PBXFileReference + name + ofxToggle.cpp + path + ../../../addons/ofxGui/src/ofxToggle.cpp + sourceTree + SOURCE_ROOT + + C70D8946940288799E82131E + + explicitFileType + sourcecode.c.h + fileEncoding + 30 + isa + PBXFileReference + name + ofxSliderGroup.h + path + ../../../addons/ofxGui/src/ofxSliderGroup.h + sourceTree + SOURCE_ROOT + + B56FE57CC35806596D38118C + + fileRef + 802251BAF1B35B1D67B32FD0 + isa + PBXBuildFile + + 802251BAF1B35B1D67B32FD0 + + explicitFileType + sourcecode.cpp.cpp + fileEncoding + 30 + isa + PBXFileReference + name + ofxSliderGroup.cpp + path + ../../../addons/ofxGui/src/ofxSliderGroup.cpp + sourceTree + SOURCE_ROOT + + 52AFA1F08C420992CAAAE648 + + explicitFileType + sourcecode.c.h + fileEncoding + 30 + isa + PBXFileReference + name + ofxSlider.h + path + ../../../addons/ofxGui/src/ofxSlider.h + sourceTree + SOURCE_ROOT + + 837220E80EB56CD44AD27F2A + + fileRef + 15F2C6477A769C03A56D1401 + isa + PBXBuildFile + + 15F2C6477A769C03A56D1401 + + explicitFileType + sourcecode.cpp.cpp + fileEncoding + 30 + isa + PBXFileReference + name + ofxSlider.cpp + path + ../../../addons/ofxGui/src/ofxSlider.cpp + sourceTree + SOURCE_ROOT + + 89449E3044D456F7DE7BEA14 + + explicitFileType + sourcecode.c.h + fileEncoding + 30 + isa + PBXFileReference + name + ofxPanel.h + path + ../../../addons/ofxGui/src/ofxPanel.h + sourceTree + SOURCE_ROOT + + F285EB3169F1566CA3D93C20 + + fileRef + E112B3AEBEA2C091BF2B40AE + isa + PBXBuildFile + + E112B3AEBEA2C091BF2B40AE + + explicitFileType + sourcecode.cpp.cpp + fileEncoding + 30 + isa + PBXFileReference + name + ofxPanel.cpp + path + ../../../addons/ofxGui/src/ofxPanel.cpp + sourceTree + SOURCE_ROOT + + B87C60311EC1FE841C1ECD89 + + explicitFileType + sourcecode.c.h + fileEncoding + 30 + isa + PBXFileReference + name + ofxLabel.h + path + ../../../addons/ofxGui/src/ofxLabel.h + sourceTree + SOURCE_ROOT + + 483908258D00B98B4BE69F07 + + fileRef + 78D67A00EB899FAC09430597 + isa + PBXBuildFile + + 78D67A00EB899FAC09430597 + + explicitFileType + sourcecode.cpp.cpp + fileEncoding + 30 + isa + PBXFileReference + name + ofxLabel.cpp + path + ../../../addons/ofxGui/src/ofxLabel.cpp + sourceTree + SOURCE_ROOT + + 1C0DA2561397A7DE0246858B + + explicitFileType + sourcecode.c.h + fileEncoding + 30 + isa + PBXFileReference + name + ofxGuiGroup.h + path + ../../../addons/ofxGui/src/ofxGuiGroup.h + sourceTree + SOURCE_ROOT + + B266578FC55D23BFEBC042E7 + + fileRef + ECF8674C7975F1063C5E30CA + isa + PBXBuildFile + + ECF8674C7975F1063C5E30CA + + explicitFileType + sourcecode.cpp.cpp + fileEncoding + 30 + isa + PBXFileReference + name + ofxGuiGroup.cpp + path + ../../../addons/ofxGui/src/ofxGuiGroup.cpp + sourceTree + SOURCE_ROOT + + 17E65988300FBD9AAA2CD0CA + + explicitFileType + sourcecode.c.h + fileEncoding + 30 + isa + PBXFileReference + name + ofxGui.h + path + ../../../addons/ofxGui/src/ofxGui.h + sourceTree + SOURCE_ROOT + + 2834D88A62CD23F3DE2C47D1 + + explicitFileType + sourcecode.c.h + fileEncoding + 30 + isa + PBXFileReference + name + ofxButton.h + path + ../../../addons/ofxGui/src/ofxButton.h + sourceTree + SOURCE_ROOT + + 5CBB2AB3A60F65431D7B555D + + fileRef + C88333E71C9457E441C33474 + isa + PBXBuildFile + + C88333E71C9457E441C33474 + + explicitFileType + sourcecode.cpp.cpp + fileEncoding + 30 + isa + PBXFileReference + name + ofxButton.cpp + path + ../../../addons/ofxGui/src/ofxButton.cpp + sourceTree + SOURCE_ROOT + + 87F26B4B24CBD428AD9EEBAA + + explicitFileType + sourcecode.c.h + fileEncoding + 30 + isa + PBXFileReference + name + ofxBaseGui.h + path + ../../../addons/ofxGui/src/ofxBaseGui.h + sourceTree + SOURCE_ROOT + + A763ED608B35AE3310251DEE + + children + + 9604B925D32EE39065747725 + 87F26B4B24CBD428AD9EEBAA + C88333E71C9457E441C33474 + 2834D88A62CD23F3DE2C47D1 + 17E65988300FBD9AAA2CD0CA + ECF8674C7975F1063C5E30CA + 1C0DA2561397A7DE0246858B + 78D67A00EB899FAC09430597 + B87C60311EC1FE841C1ECD89 + E112B3AEBEA2C091BF2B40AE + 89449E3044D456F7DE7BEA14 + 15F2C6477A769C03A56D1401 + 52AFA1F08C420992CAAAE648 + 802251BAF1B35B1D67B32FD0 + C70D8946940288799E82131E + 907C5B5E104864A2D3A25745 + 0A1DAC09F322AE313A40706D + + isa + PBXGroup + name + src + sourceTree + <group> + + 480A780D8D0308AE4A368801 + + children + + A763ED608B35AE3310251DEE + + isa + PBXGroup + name + ofxGui + sourceTree + <group> + + 856AA354D08AB4B323081444 + + fileRef + 9604B925D32EE39065747725 + isa + PBXBuildFile + + 9604B925D32EE39065747725 + + explicitFileType + sourcecode.cpp.cpp + fileEncoding + 30 + isa + PBXFileReference + name + ofxBaseGui.cpp + path + ../../../addons/ofxGui/src/ofxBaseGui.cpp + sourceTree + SOURCE_ROOT + + 61711D3347CDFB64FA721D44 + + children + + 85307C7E25682171EB5DC998 + + isa + PBXGroup + name + include + sourceTree + <group> + + FBBD8BC6A172852ABC6FF337 + + children + + 61711D3347CDFB64FA721D44 + + isa + PBXGroup + name + ccv + sourceTree + <group> + + 88578B999DE5C1A63035FFCB + + children + + FBBD8BC6A172852ABC6FF337 + + isa + PBXGroup + name + libs + sourceTree + <group> + + 85307C7E25682171EB5DC998 + + explicitFileType + sourcecode.c.h + fileEncoding + 30 + isa + PBXFileReference + name + ccv.h + path + ../../../addons/ofxCcv/libs/ccv/include/ccv.h + sourceTree + SOURCE_ROOT + + DE75137AC67515D3FC3624C7 + + explicitFileType + sourcecode.c.h + fileEncoding + 30 + isa + PBXFileReference + name + ofxCcv.h + path + ../../../addons/ofxCcv/src/ofxCcv.h + sourceTree + SOURCE_ROOT + + CBD10E62E7E6E2DA133E22ED + + children + + EA24FDDC5E989AF2191488A3 + DE75137AC67515D3FC3624C7 + + isa + PBXGroup + name + src + sourceTree + <group> + + 795B0427BBCB5647F47B56C8 + + children + + CBD10E62E7E6E2DA133E22ED + 88578B999DE5C1A63035FFCB + + isa + PBXGroup + name + ofxCcv + sourceTree + <group> + + 6A5A3AF2E51903FC129D6939 + + fileRef + EA24FDDC5E989AF2191488A3 + isa + PBXBuildFile + + EA24FDDC5E989AF2191488A3 + + explicitFileType + sourcecode.cpp.cpp + fileEncoding + 30 + isa + PBXFileReference + name + ofxCcv.cpp + path + ../../../addons/ofxCcv/src/ofxCcv.cpp + sourceTree + SOURCE_ROOT + + BB4B014C10F69532006C3DED + + children + + 795B0427BBCB5647F47B56C8 + 480A780D8D0308AE4A368801 + A2C3A140678F172C21702046 + + isa + PBXGroup + name + addons + sourceTree + <group> + + 6948EE371B920CB800B5AC1A + + children + + isa + PBXGroup + name + local_addons + sourceTree + <group> + + E4328143138ABC890047C5CB + + isa + PBXFileReference + lastKnownFileType + wrapper.pb-project + name + openFrameworksLib.xcodeproj + path + ../../../libs/openFrameworksCompiled/project/osx/openFrameworksLib.xcodeproj + sourceTree + SOURCE_ROOT + + E4328144138ABC890047C5CB + + children + + E4328148138ABC890047C5CB + + isa + PBXGroup + name + Products + sourceTree + <group> + + E4328147138ABC890047C5CB + + containerPortal + E4328143138ABC890047C5CB + isa + PBXContainerItemProxy + proxyType + 2 + remoteGlobalIDString + E4B27C1510CBEB8E00536013 + remoteInfo + openFrameworks + + E4328148138ABC890047C5CB + + fileType + archive.ar + isa + PBXReferenceProxy + path + openFrameworksDebug.a + remoteRef + E4328147138ABC890047C5CB + sourceTree + BUILT_PRODUCTS_DIR + + E4328149138ABC9F0047C5CB + + fileRef + E4328148138ABC890047C5CB + isa + PBXBuildFile + + E4B69B4A0A3A1720003C02F2 + + children + + E4B6FCAD0C3E899E008CF71C + E4EB6923138AFD0F00A09F29 + E4B69E1C0A3A1BDC003C02F2 + E4EEC9E9138DF44700A80321 + BB4B014C10F69532006C3DED + 6948EE371B920CB800B5AC1A + E4B69B5B0A3A1756003C02F2 + + isa + PBXGroup + sourceTree + <group> + + E4B69B4C0A3A1720003C02F2 + + attributes + + LastUpgradeCheck + 0600 + + buildConfigurationList + E4B69B4D0A3A1720003C02F2 + compatibilityVersion + Xcode 3.2 + developmentRegion + English + hasScannedForEncodings + 0 + isa + PBXProject + knownRegions + + English + Japanese + French + German + + mainGroup + E4B69B4A0A3A1720003C02F2 + productRefGroup + E4B69B4A0A3A1720003C02F2 + projectDirPath + + projectReferences + + + ProductGroup + E4328144138ABC890047C5CB + ProjectRef + E4328143138ABC890047C5CB + + + projectRoot + + targets + + E4B69B5A0A3A1756003C02F2 + + + E4B69B4D0A3A1720003C02F2 + + buildConfigurations + + E4B69B4E0A3A1720003C02F2 + E4B69B4F0A3A1720003C02F2 + + defaultConfigurationIsVisible + 0 + defaultConfigurationName + Release + isa + XCConfigurationList + + E4B69B4E0A3A1720003C02F2 + + baseConfigurationReference + E4EB6923138AFD0F00A09F29 + buildSettings + + OTHER_LDFLAGS + + $(OF_CORE_FRAMEWORKS) $(OF_CORE_LIBS) + ../../../addons/ofxCcv/libs/ccv/lib/osx/ccv.a + + HEADER_SEARCH_PATHS + + $(OF_CORE_HEADERS) + src + ../../../addons/ofxCcv/libs + ../../../addons/ofxCcv/libs/ccv + ../../../addons/ofxCcv/libs/ccv/include + ../../../addons/ofxCcv/libs/ccv/lib + ../../../addons/ofxCcv/libs/ccv/lib/osx + ../../../addons/ofxCcv/src + ../../../addons/ofxGui/src + ../../../addons/ofxTSNE/src + ../../../addons/ofxTSNE/src/bhtsne + + CONFIGURATION_BUILD_DIR + $(SRCROOT)/bin/ + COPY_PHASE_STRIP + NO + DEAD_CODE_STRIPPING + YES + GCC_AUTO_VECTORIZATION + YES + GCC_ENABLE_SSE3_EXTENSIONS + YES + GCC_ENABLE_SUPPLEMENTAL_SSE3_INSTRUCTIONS + YES + GCC_INLINES_ARE_PRIVATE_EXTERN + NO + GCC_OPTIMIZATION_LEVEL + 0 + GCC_SYMBOLS_PRIVATE_EXTERN + NO + GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS + YES + GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO + NO + GCC_WARN_ALLOW_INCOMPLETE_PROTOCOL + NO + GCC_WARN_UNINITIALIZED_AUTOS + NO + GCC_WARN_UNUSED_VALUE + NO + GCC_WARN_UNUSED_VARIABLE + NO + MACOSX_DEPLOYMENT_TARGET + 10.8 + ONLY_ACTIVE_ARCH + YES + OTHER_CPLUSPLUSFLAGS + + -D__MACOSX_CORE__ + -mtune=native + + SDKROOT + macosx + + isa + XCBuildConfiguration + name + Debug + + E4B69B4F0A3A1720003C02F2 + + baseConfigurationReference + E4EB6923138AFD0F00A09F29 + buildSettings + + OTHER_LDFLAGS + + $(OF_CORE_FRAMEWORKS) $(OF_CORE_LIBS) + ../../../addons/ofxCcv/libs/ccv/lib/osx/ccv.a + + HEADER_SEARCH_PATHS + + $(OF_CORE_HEADERS) + src + ../../../addons/ofxCcv/libs + ../../../addons/ofxCcv/libs/ccv + ../../../addons/ofxCcv/libs/ccv/include + ../../../addons/ofxCcv/libs/ccv/lib + ../../../addons/ofxCcv/libs/ccv/lib/osx + ../../../addons/ofxCcv/src + ../../../addons/ofxGui/src + ../../../addons/ofxTSNE/src + ../../../addons/ofxTSNE/src/bhtsne + + CONFIGURATION_BUILD_DIR + $(SRCROOT)/bin/ + COPY_PHASE_STRIP + YES + DEAD_CODE_STRIPPING + YES + GCC_AUTO_VECTORIZATION + YES + GCC_ENABLE_SSE3_EXTENSIONS + YES + GCC_ENABLE_SUPPLEMENTAL_SSE3_INSTRUCTIONS + YES + GCC_INLINES_ARE_PRIVATE_EXTERN + NO + GCC_OPTIMIZATION_LEVEL + 3 + GCC_SYMBOLS_PRIVATE_EXTERN + NO + GCC_UNROLL_LOOPS + YES + GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS + YES + GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO + NO + GCC_WARN_ALLOW_INCOMPLETE_PROTOCOL + NO + GCC_WARN_UNINITIALIZED_AUTOS + NO + GCC_WARN_UNUSED_VALUE + NO + GCC_WARN_UNUSED_VARIABLE + NO + MACOSX_DEPLOYMENT_TARGET + 10.8 + OTHER_CPLUSPLUSFLAGS + + -D__MACOSX_CORE__ + -mtune=native + + SDKROOT + macosx + + isa + XCBuildConfiguration + name + Release + + E4B69B580A3A1756003C02F2 + + buildActionMask + 2147483647 + files + + E4B69E200A3A1BDC003C02F2 + E4B69E210A3A1BDC003C02F2 + 6A5A3AF2E51903FC129D6939 + 856AA354D08AB4B323081444 + 5CBB2AB3A60F65431D7B555D + B266578FC55D23BFEBC042E7 + 483908258D00B98B4BE69F07 + F285EB3169F1566CA3D93C20 + 837220E80EB56CD44AD27F2A + B56FE57CC35806596D38118C + 1CD33E884D9E3358252E82A1 + 3F59654CA5CEEFA57C8D09DC + BF438D1989EA8474A24FC372 + 51A4134BF889D553064DCD2E + + isa + PBXSourcesBuildPhase + runOnlyForDeploymentPostprocessing + 0 + + E4B69B590A3A1756003C02F2 + + buildActionMask + 2147483647 + files + + E4328149138ABC9F0047C5CB + + isa + PBXFrameworksBuildPhase + runOnlyForDeploymentPostprocessing + 0 + + E4B69B5A0A3A1756003C02F2 + + buildConfigurationList + E4B69B5F0A3A1757003C02F2 + buildPhases + + E4B69B580A3A1756003C02F2 + E4B69B590A3A1756003C02F2 + E4B6FFFD0C3F9AB9008CF71C + E4C2427710CC5ABF004149E2 + + buildRules + + dependencies + + E4EEB9AC138B136A00A80321 + + isa + PBXNativeTarget + name + example-images + productName + myOFApp + productReference + E4B69B5B0A3A1756003C02F2 + productType + com.apple.product-type.application + + E4B69B5B0A3A1756003C02F2 + + explicitFileType + wrapper.application + includeInIndex + 0 + isa + PBXFileReference + path + example-imagesDebug.app + sourceTree + BUILT_PRODUCTS_DIR + + E4B69B5F0A3A1757003C02F2 + + buildConfigurations + + E4B69B600A3A1757003C02F2 + E4B69B610A3A1757003C02F2 + + defaultConfigurationIsVisible + 0 + defaultConfigurationName + Release + isa + XCConfigurationList + + E4B69B600A3A1757003C02F2 + + baseConfigurationReference + E4EB6923138AFD0F00A09F29 + buildSettings + + OTHER_LDFLAGS + + $(OF_CORE_FRAMEWORKS) $(OF_CORE_LIBS) + ../../../addons/ofxCcv/libs/ccv/lib/osx/ccv.a + + HEADER_SEARCH_PATHS + + $(OF_CORE_HEADERS) + src + ../../../addons/ofxCcv/libs + ../../../addons/ofxCcv/libs/ccv + ../../../addons/ofxCcv/libs/ccv/include + ../../../addons/ofxCcv/libs/ccv/lib + ../../../addons/ofxCcv/libs/ccv/lib/osx + ../../../addons/ofxCcv/src + ../../../addons/ofxGui/src + ../../../addons/ofxTSNE/src + ../../../addons/ofxTSNE/src/bhtsne + + COMBINE_HIDPI_IMAGES + YES + COPY_PHASE_STRIP + NO + FRAMEWORK_SEARCH_PATHS + + $(inherited) + $(FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_1) + + FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_1 + "$(SRCROOT)/../../../libs/glut/lib/osx" + GCC_DYNAMIC_NO_PIC + NO + GCC_GENERATE_DEBUGGING_SYMBOLS + YES + GCC_MODEL_TUNING + NONE + ICON + $(ICON_NAME_DEBUG) + ICON_FILE + $(ICON_FILE_PATH)$(ICON) + INFOPLIST_FILE + openFrameworks-Info.plist + INSTALL_PATH + $(HOME)/Applications + LIBRARY_SEARCH_PATHS + $(inherited) + PRODUCT_NAME + $(TARGET_NAME)Debug + WRAPPER_EXTENSION + app + + isa + XCBuildConfiguration + name + Debug + + E4B69B610A3A1757003C02F2 + + baseConfigurationReference + E4EB6923138AFD0F00A09F29 + buildSettings + + OTHER_LDFLAGS + + $(OF_CORE_FRAMEWORKS) $(OF_CORE_LIBS) + ../../../addons/ofxCcv/libs/ccv/lib/osx/ccv.a + + HEADER_SEARCH_PATHS + + $(OF_CORE_HEADERS) + src + ../../../addons/ofxCcv/libs + ../../../addons/ofxCcv/libs/ccv + ../../../addons/ofxCcv/libs/ccv/include + ../../../addons/ofxCcv/libs/ccv/lib + ../../../addons/ofxCcv/libs/ccv/lib/osx + ../../../addons/ofxCcv/src + ../../../addons/ofxGui/src + ../../../addons/ofxTSNE/src + ../../../addons/ofxTSNE/src/bhtsne + + COMBINE_HIDPI_IMAGES + YES + COPY_PHASE_STRIP + YES + FRAMEWORK_SEARCH_PATHS + + $(inherited) + $(FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_1) + + FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_1 + "$(SRCROOT)/../../../libs/glut/lib/osx" + GCC_GENERATE_DEBUGGING_SYMBOLS + YES + GCC_MODEL_TUNING + NONE + ICON + $(ICON_NAME_RELEASE) + ICON_FILE + $(ICON_FILE_PATH)$(ICON) + INFOPLIST_FILE + openFrameworks-Info.plist + INSTALL_PATH + $(HOME)/Applications + LIBRARY_SEARCH_PATHS + $(inherited) + PRODUCT_NAME + $(TARGET_NAME) + WRAPPER_EXTENSION + app + baseConfigurationReference + E4EB6923138AFD0F00A09F29 + + isa + XCBuildConfiguration + name + Release + + E4B69E1C0A3A1BDC003C02F2 + + children + + E4B69E1D0A3A1BDC003C02F2 + E4B69E1E0A3A1BDC003C02F2 + E4B69E1F0A3A1BDC003C02F2 + + isa + PBXGroup + path + src + sourceTree + SOURCE_ROOT + + E4B69E1D0A3A1BDC003C02F2 + + fileEncoding + 30 + isa + PBXFileReference + lastKnownFileType + sourcecode.cpp.cpp + name + main.cpp + path + src/main.cpp + sourceTree + SOURCE_ROOT + + E4B69E1E0A3A1BDC003C02F2 + + explicitFileType + sourcecode.cpp.cpp + fileEncoding + 30 + isa + PBXFileReference + name + ofApp.cpp + path + src/ofApp.cpp + sourceTree + SOURCE_ROOT + + E4B69E1F0A3A1BDC003C02F2 + + fileEncoding + 30 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + ofApp.h + path + src/ofApp.h + sourceTree + SOURCE_ROOT + + E4B69E200A3A1BDC003C02F2 + + fileRef + E4B69E1D0A3A1BDC003C02F2 + isa + PBXBuildFile + + E4B69E210A3A1BDC003C02F2 + + fileRef + E4B69E1E0A3A1BDC003C02F2 + isa + PBXBuildFile + + E4B6FCAD0C3E899E008CF71C + + fileEncoding + 30 + isa + PBXFileReference + lastKnownFileType + text.plist.xml + path + openFrameworks-Info.plist + sourceTree + <group> + + E4B6FFFD0C3F9AB9008CF71C + + buildActionMask + 2147483647 + files + + inputPaths + + isa + PBXShellScriptBuildPhase + outputPaths + + runOnlyForDeploymentPostprocessing + 0 + shellPath + /bin/sh + shellScript + rsync -aved ../../../libs/fmodex/lib/osx/libfmodex.dylib "$TARGET_BUILD_DIR/$PRODUCT_NAME.app/Contents/MacOS/"; install_name_tool -change ./libfmodex.dylib @executable_path/libfmodex.dylib "$TARGET_BUILD_DIR/$PRODUCT_NAME.app/Contents/MacOS/$PRODUCT_NAME"; +mkdir -p "$TARGET_BUILD_DIR/$PRODUCT_NAME.app/Contents/Resources/" +rsync -aved "$ICON_FILE" "$TARGET_BUILD_DIR/$PRODUCT_NAME.app/Contents/Resources/" +rsync -aved ../../../libs/glut/lib/osx/GLUT.framework "$TARGET_BUILD_DIR/$PRODUCT_NAME.app/Contents/Frameworks/" + + + E4C2427710CC5ABF004149E2 + + buildActionMask + 2147483647 + dstPath + + dstSubfolderSpec + 10 + files + + isa + PBXCopyFilesBuildPhase + runOnlyForDeploymentPostprocessing + 0 + + E4EB691F138AFCF100A09F29 + + fileEncoding + 4 + isa + PBXFileReference + lastKnownFileType + text.xcconfig + name + CoreOF.xcconfig + path + ../../../libs/openFrameworksCompiled/project/osx/CoreOF.xcconfig + sourceTree + SOURCE_ROOT + + E4EB6923138AFD0F00A09F29 + + fileEncoding + 4 + isa + PBXFileReference + lastKnownFileType + text.xcconfig + path + Project.xcconfig + sourceTree + <group> + + E4EEB9AB138B136A00A80321 + + containerPortal + E4328143138ABC890047C5CB + isa + PBXContainerItemProxy + proxyType + 1 + remoteGlobalIDString + E4B27C1410CBEB8E00536013 + remoteInfo + openFrameworks + + E4EEB9AC138B136A00A80321 + + isa + PBXTargetDependency + name + openFrameworks + targetProxy + E4EEB9AB138B136A00A80321 + + E4EEC9E9138DF44700A80321 + + children + + E4EB691F138AFCF100A09F29 + E4328143138ABC890047C5CB + + isa + PBXGroup + name + openFrameworks + sourceTree + <group> + + + rootObject + E4B69B4C0A3A1720003C02F2 + + diff --git a/example-images/example-images.xcodeproj/project.xcworkspace/xcuserdata/gene.xcuserdatad/UserInterfaceState.xcuserstate b/example-images/example-images.xcodeproj/project.xcworkspace/xcuserdata/gene.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000000000000000000000000000000000000..b50d009672bbaef10d0750911dd6e56bad184ca9 GIT binary patch literal 16063 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/example-images/example-images.xcodeproj/xcshareddata/xcschemes/example-images Release.xcscheme b/example-images/example-images.xcodeproj/xcshareddata/xcschemes/example-images Release.xcscheme new file mode 100644 index 0000000..0bdfdc1 --- /dev/null +++ b/example-images/example-images.xcodeproj/xcshareddata/xcschemes/example-images Release.xcscheme @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/example-images/example-images.xcodeproj/xcuserdata/gene.xcuserdatad/xcschemes/xcschememanagement.plist b/example-images/example-images.xcodeproj/xcuserdata/gene.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..13dfb6e --- /dev/null +++ b/example-images/example-images.xcodeproj/xcuserdata/gene.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + SuppressBuildableAutocreation + + E4B69B5A0A3A1756003C02F2 + + primary + + + + + diff --git a/example-images/openFrameworks-Info.plist b/example-images/openFrameworks-Info.plist new file mode 100644 index 0000000..8d64d2b --- /dev/null +++ b/example-images/openFrameworks-Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + cc.openFrameworks.ofapp + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + APPL + CFBundleSignature + ???? + CFBundleVersion + 1.0 + CFBundleIconFile + ${ICON} + + diff --git a/example-images/src/main.cpp b/example-images/src/main.cpp new file mode 100644 index 0000000..e57370b --- /dev/null +++ b/example-images/src/main.cpp @@ -0,0 +1,13 @@ +#include "ofMain.h" +#include "ofApp.h" + +//======================================================================== +int main( ){ + ofSetupOpenGL(1024,768,OF_WINDOW); // <-------- setup the GL context + + // this kicks off the running of my app + // can be OF_WINDOW or OF_FULLSCREEN + // pass in width and height too: + ofRunApp(new ofApp()); + +} diff --git a/example-images/src/ofApp.cpp b/example-images/src/ofApp.cpp new file mode 100644 index 0000000..2e21baa --- /dev/null +++ b/example-images/src/ofApp.cpp @@ -0,0 +1,131 @@ +#include "ofApp.h" + +//-------------------------------------------------------------- +void ofApp::setup(){ + + // load all the images + ofLog() << "Gathering images..."; + ofDirectory dir; + int nFiles = dir.listDir(ofToDataPath("images/")); + if(nFiles) { + for(int i=0; i encoding = ccv.encode(images[i], ccv.numLayers()-1); + encodings.push_back(encoding); + } + + // run t-SNE and load image points to imagePoints + ofLog() << "Run t-SNE on images"; + imagePoints = tsne.run(encodings, 2, 25, 0.1, true); + + // make the images the same size + for (int i=0; i images; + vector > imagePoints; + vector > encodings; + + ofxPanel gui; + ofParameter scale; + ofParameter imageSize; +}; diff --git a/example/Makefile b/example/Makefile new file mode 100644 index 0000000..8d8e4c0 --- /dev/null +++ b/example/Makefile @@ -0,0 +1,13 @@ +# Attempt to load a config.make file. +# If none is found, project defaults in config.project.make will be used. +ifneq ($(wildcard config.make),) + include config.make +endif + +# make sure the the OF_ROOT location is defined +ifndef OF_ROOT + OF_ROOT=$(realpath ../../..) +endif + +# call the project makefile! +include $(OF_ROOT)/libs/openFrameworksCompiled/project/makefileCommon/compile.project.mk diff --git a/example/Project.xcconfig b/example/Project.xcconfig new file mode 100644 index 0000000..e570b15 --- /dev/null +++ b/example/Project.xcconfig @@ -0,0 +1,17 @@ +//THE PATH TO THE ROOT OF OUR OF PATH RELATIVE TO THIS PROJECT. +//THIS NEEDS TO BE DEFINED BEFORE CoreOF.xcconfig IS INCLUDED +OF_PATH = ../../.. + +//THIS HAS ALL THE HEADER AND LIBS FOR OF CORE +#include "../../../libs/openFrameworksCompiled/project/osx/CoreOF.xcconfig" + +//ICONS - NEW IN 0072 +ICON_NAME_DEBUG = icon-debug.icns +ICON_NAME_RELEASE = icon.icns +ICON_FILE_PATH = $(OF_PATH)/libs/openFrameworksCompiled/project/osx/ + +//IF YOU WANT AN APP TO HAVE A CUSTOM ICON - PUT THEM IN YOUR DATA FOLDER AND CHANGE ICON_FILE_PATH to: +//ICON_FILE_PATH = bin/data/ + +OTHER_LDFLAGS = $(OF_CORE_LIBS) $(OF_CORE_FRAMEWORKS) +HEADER_SEARCH_PATHS = $(OF_CORE_HEADERS) diff --git a/example/addons.make b/example/addons.make new file mode 100644 index 0000000..aaf6171 --- /dev/null +++ b/example/addons.make @@ -0,0 +1 @@ +ofxTSNE diff --git a/example/config.make b/example/config.make new file mode 100644 index 0000000..df10f64 --- /dev/null +++ b/example/config.make @@ -0,0 +1,142 @@ +################################################################################ +# CONFIGURE PROJECT MAKEFILE (optional) +# This file is where we make project specific configurations. +################################################################################ + +################################################################################ +# OF ROOT +# The location of your root openFrameworks installation +# (default) OF_ROOT = ../../.. +################################################################################ +# OF_ROOT = ../../.. + +################################################################################ +# PROJECT ROOT +# The location of the project - a starting place for searching for files +# (default) PROJECT_ROOT = . (this directory) +# +################################################################################ +# PROJECT_ROOT = . + +################################################################################ +# PROJECT SPECIFIC CHECKS +# This is a project defined section to create internal makefile flags to +# conditionally enable or disable the addition of various features within +# this makefile. For instance, if you want to make changes based on whether +# GTK is installed, one might test that here and create a variable to check. +################################################################################ +# None + +################################################################################ +# PROJECT EXTERNAL SOURCE PATHS +# These are fully qualified paths that are not within the PROJECT_ROOT folder. +# Like source folders in the PROJECT_ROOT, these paths are subject to +# exlclusion via the PROJECT_EXLCUSIONS list. +# +# (default) PROJECT_EXTERNAL_SOURCE_PATHS = (blank) +# +# Note: Leave a leading space when adding list items with the += operator +################################################################################ +# PROJECT_EXTERNAL_SOURCE_PATHS = + +################################################################################ +# PROJECT EXCLUSIONS +# These makefiles assume that all folders in your current project directory +# and any listed in the PROJECT_EXTERNAL_SOURCH_PATHS are are valid locations +# to look for source code. The any folders or files that match any of the +# items in the PROJECT_EXCLUSIONS list below will be ignored. +# +# Each item in the PROJECT_EXCLUSIONS list will be treated as a complete +# string unless teh user adds a wildcard (%) operator to match subdirectories. +# GNU make only allows one wildcard for matching. The second wildcard (%) is +# treated literally. +# +# (default) PROJECT_EXCLUSIONS = (blank) +# +# Will automatically exclude the following: +# +# $(PROJECT_ROOT)/bin% +# $(PROJECT_ROOT)/obj% +# $(PROJECT_ROOT)/%.xcodeproj +# +# Note: Leave a leading space when adding list items with the += operator +################################################################################ +# PROJECT_EXCLUSIONS = + +################################################################################ +# PROJECT LINKER FLAGS +# These flags will be sent to the linker when compiling the executable. +# +# (default) PROJECT_LDFLAGS = -Wl,-rpath=./libs +# +# Note: Leave a leading space when adding list items with the += operator +################################################################################ + +# Currently, shared libraries that are needed are copied to the +# $(PROJECT_ROOT)/bin/libs directory. The following LDFLAGS tell the linker to +# add a runtime path to search for those shared libraries, since they aren't +# incorporated directly into the final executable application binary. +# TODO: should this be a default setting? +# PROJECT_LDFLAGS=-Wl,-rpath=./libs + +################################################################################ +# PROJECT DEFINES +# Create a space-delimited list of DEFINES. The list will be converted into +# CFLAGS with the "-D" flag later in the makefile. +# +# (default) PROJECT_DEFINES = (blank) +# +# Note: Leave a leading space when adding list items with the += operator +################################################################################ +# PROJECT_DEFINES = + +################################################################################ +# PROJECT CFLAGS +# This is a list of fully qualified CFLAGS required when compiling for this +# project. These CFLAGS will be used IN ADDITION TO the PLATFORM_CFLAGS +# defined in your platform specific core configuration files. These flags are +# presented to the compiler BEFORE the PROJECT_OPTIMIZATION_CFLAGS below. +# +# (default) PROJECT_CFLAGS = (blank) +# +# Note: Before adding PROJECT_CFLAGS, note that the PLATFORM_CFLAGS defined in +# your platform specific configuration file will be applied by default and +# further flags here may not be needed. +# +# Note: Leave a leading space when adding list items with the += operator +################################################################################ +# PROJECT_CFLAGS = + +################################################################################ +# PROJECT OPTIMIZATION CFLAGS +# These are lists of CFLAGS that are target-specific. While any flags could +# be conditionally added, they are usually limited to optimization flags. +# These flags are added BEFORE the PROJECT_CFLAGS. +# +# PROJECT_OPTIMIZATION_CFLAGS_RELEASE flags are only applied to RELEASE targets. +# +# (default) PROJECT_OPTIMIZATION_CFLAGS_RELEASE = (blank) +# +# PROJECT_OPTIMIZATION_CFLAGS_DEBUG flags are only applied to DEBUG targets. +# +# (default) PROJECT_OPTIMIZATION_CFLAGS_DEBUG = (blank) +# +# Note: Before adding PROJECT_OPTIMIZATION_CFLAGS, please note that the +# PLATFORM_OPTIMIZATION_CFLAGS defined in your platform specific configuration +# file will be applied by default and further optimization flags here may not +# be needed. +# +# Note: Leave a leading space when adding list items with the += operator +################################################################################ +# PROJECT_OPTIMIZATION_CFLAGS_RELEASE = +# PROJECT_OPTIMIZATION_CFLAGS_DEBUG = + +################################################################################ +# PROJECT COMPILERS +# Custom compilers can be set for CC and CXX +# (default) PROJECT_CXX = (blank) +# (default) PROJECT_CC = (blank) +# Note: Leave a leading space when adding list items with the += operator +################################################################################ +# PROJECT_CXX = +# PROJECT_CC = diff --git a/example/example.xcodeproj/project.pbxproj b/example/example.xcodeproj/project.pbxproj new file mode 100644 index 0000000..b600b7c --- /dev/null +++ b/example/example.xcodeproj/project.pbxproj @@ -0,0 +1,837 @@ + + + + archiveVersion + 1 + classes + + objectVersion + 46 + objects + + A18A04F43F1A3D8641B0D261 + + explicitFileType + sourcecode.c.h + fileEncoding + 30 + isa + PBXFileReference + name + ofxTSNE.h + path + ../../../addons/ofxTSNE/src/ofxTSNE.h + sourceTree + SOURCE_ROOT + + 51A4134BF889D553064DCD2E + + fileRef + D985BBD882BD1582C610B30C + isa + PBXBuildFile + + D985BBD882BD1582C610B30C + + explicitFileType + sourcecode.cpp.cpp + fileEncoding + 30 + isa + PBXFileReference + name + ofxTSNE.cpp + path + ../../../addons/ofxTSNE/src/ofxTSNE.cpp + sourceTree + SOURCE_ROOT + + 1F7816F26F468BAC49B9ADD0 + + explicitFileType + sourcecode.c.h + fileEncoding + 30 + isa + PBXFileReference + name + vptree.h + path + ../../../addons/ofxTSNE/src/bhtsne/vptree.h + sourceTree + SOURCE_ROOT + + 54DCDCE01CF02EC24329509D + + explicitFileType + sourcecode.c.h + fileEncoding + 30 + isa + PBXFileReference + name + tsne.h + path + ../../../addons/ofxTSNE/src/bhtsne/tsne.h + sourceTree + SOURCE_ROOT + + BF438D1989EA8474A24FC372 + + fileRef + C5EC22EBF07D1C6136CAFF40 + isa + PBXBuildFile + + C5EC22EBF07D1C6136CAFF40 + + explicitFileType + sourcecode.cpp.cpp + fileEncoding + 30 + isa + PBXFileReference + name + tsne.cpp + path + ../../../addons/ofxTSNE/src/bhtsne/tsne.cpp + sourceTree + SOURCE_ROOT + + D361105E69E9248C8CDD4024 + + explicitFileType + sourcecode.c.h + fileEncoding + 30 + isa + PBXFileReference + name + sptree.h + path + ../../../addons/ofxTSNE/src/bhtsne/sptree.h + sourceTree + SOURCE_ROOT + + C289F16CCC88C08ABEBC3CE6 + + children + + 29CFCFEEAF44F7849B7A8E2C + D361105E69E9248C8CDD4024 + C5EC22EBF07D1C6136CAFF40 + 54DCDCE01CF02EC24329509D + 1F7816F26F468BAC49B9ADD0 + + isa + PBXGroup + name + bhtsne + sourceTree + <group> + + 84110FEC26026E23FCEDE55A + + children + + C289F16CCC88C08ABEBC3CE6 + D985BBD882BD1582C610B30C + A18A04F43F1A3D8641B0D261 + + isa + PBXGroup + name + src + sourceTree + <group> + + A2C3A140678F172C21702046 + + children + + 84110FEC26026E23FCEDE55A + + isa + PBXGroup + name + ofxTSNE + sourceTree + <group> + + 3F59654CA5CEEFA57C8D09DC + + fileRef + 29CFCFEEAF44F7849B7A8E2C + isa + PBXBuildFile + + 29CFCFEEAF44F7849B7A8E2C + + explicitFileType + sourcecode.cpp.cpp + fileEncoding + 30 + isa + PBXFileReference + name + sptree.cpp + path + ../../../addons/ofxTSNE/src/bhtsne/sptree.cpp + sourceTree + SOURCE_ROOT + + BB4B014C10F69532006C3DED + + children + + A2C3A140678F172C21702046 + + isa + PBXGroup + name + addons + sourceTree + <group> + + 6948EE371B920CB800B5AC1A + + children + + isa + PBXGroup + name + local_addons + sourceTree + <group> + + E4328143138ABC890047C5CB + + isa + PBXFileReference + lastKnownFileType + wrapper.pb-project + name + openFrameworksLib.xcodeproj + path + ../../../libs/openFrameworksCompiled/project/osx/openFrameworksLib.xcodeproj + sourceTree + SOURCE_ROOT + + E4328144138ABC890047C5CB + + children + + E4328148138ABC890047C5CB + + isa + PBXGroup + name + Products + sourceTree + <group> + + E4328147138ABC890047C5CB + + containerPortal + E4328143138ABC890047C5CB + isa + PBXContainerItemProxy + proxyType + 2 + remoteGlobalIDString + E4B27C1510CBEB8E00536013 + remoteInfo + openFrameworks + + E4328148138ABC890047C5CB + + fileType + archive.ar + isa + PBXReferenceProxy + path + openFrameworksDebug.a + remoteRef + E4328147138ABC890047C5CB + sourceTree + BUILT_PRODUCTS_DIR + + E4328149138ABC9F0047C5CB + + fileRef + E4328148138ABC890047C5CB + isa + PBXBuildFile + + E4B69B4A0A3A1720003C02F2 + + children + + E4B6FCAD0C3E899E008CF71C + E4EB6923138AFD0F00A09F29 + E4B69E1C0A3A1BDC003C02F2 + E4EEC9E9138DF44700A80321 + BB4B014C10F69532006C3DED + 6948EE371B920CB800B5AC1A + E4B69B5B0A3A1756003C02F2 + + isa + PBXGroup + sourceTree + <group> + + E4B69B4C0A3A1720003C02F2 + + attributes + + LastUpgradeCheck + 0600 + + buildConfigurationList + E4B69B4D0A3A1720003C02F2 + compatibilityVersion + Xcode 3.2 + developmentRegion + English + hasScannedForEncodings + 0 + isa + PBXProject + knownRegions + + English + Japanese + French + German + + mainGroup + E4B69B4A0A3A1720003C02F2 + productRefGroup + E4B69B4A0A3A1720003C02F2 + projectDirPath + + projectReferences + + + ProductGroup + E4328144138ABC890047C5CB + ProjectRef + E4328143138ABC890047C5CB + + + projectRoot + + targets + + E4B69B5A0A3A1756003C02F2 + + + E4B69B4D0A3A1720003C02F2 + + buildConfigurations + + E4B69B4E0A3A1720003C02F2 + E4B69B4F0A3A1720003C02F2 + + defaultConfigurationIsVisible + 0 + defaultConfigurationName + Release + isa + XCConfigurationList + + E4B69B4E0A3A1720003C02F2 + + baseConfigurationReference + E4EB6923138AFD0F00A09F29 + buildSettings + + HEADER_SEARCH_PATHS + + $(OF_CORE_HEADERS) + ../../../addons/ofxTSNE/src + ../../../addons/ofxTSNE/src/bhtsne + + CONFIGURATION_BUILD_DIR + $(SRCROOT)/bin/ + COPY_PHASE_STRIP + NO + DEAD_CODE_STRIPPING + YES + GCC_AUTO_VECTORIZATION + YES + GCC_ENABLE_SSE3_EXTENSIONS + YES + GCC_ENABLE_SUPPLEMENTAL_SSE3_INSTRUCTIONS + YES + GCC_INLINES_ARE_PRIVATE_EXTERN + NO + GCC_OPTIMIZATION_LEVEL + 0 + GCC_SYMBOLS_PRIVATE_EXTERN + NO + GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS + YES + GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO + NO + GCC_WARN_ALLOW_INCOMPLETE_PROTOCOL + NO + GCC_WARN_UNINITIALIZED_AUTOS + NO + GCC_WARN_UNUSED_VALUE + NO + GCC_WARN_UNUSED_VARIABLE + NO + MACOSX_DEPLOYMENT_TARGET + 10.8 + ONLY_ACTIVE_ARCH + YES + OTHER_CPLUSPLUSFLAGS + + -D__MACOSX_CORE__ + -mtune=native + + SDKROOT + macosx + + isa + XCBuildConfiguration + name + Debug + + E4B69B4F0A3A1720003C02F2 + + baseConfigurationReference + E4EB6923138AFD0F00A09F29 + buildSettings + + HEADER_SEARCH_PATHS + + $(OF_CORE_HEADERS) + ../../../addons/ofxTSNE/src + ../../../addons/ofxTSNE/src/bhtsne + + CONFIGURATION_BUILD_DIR + $(SRCROOT)/bin/ + COPY_PHASE_STRIP + YES + DEAD_CODE_STRIPPING + YES + GCC_AUTO_VECTORIZATION + YES + GCC_ENABLE_SSE3_EXTENSIONS + YES + GCC_ENABLE_SUPPLEMENTAL_SSE3_INSTRUCTIONS + YES + GCC_INLINES_ARE_PRIVATE_EXTERN + NO + GCC_OPTIMIZATION_LEVEL + 3 + GCC_SYMBOLS_PRIVATE_EXTERN + NO + GCC_UNROLL_LOOPS + YES + GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS + YES + GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO + NO + GCC_WARN_ALLOW_INCOMPLETE_PROTOCOL + NO + GCC_WARN_UNINITIALIZED_AUTOS + NO + GCC_WARN_UNUSED_VALUE + NO + GCC_WARN_UNUSED_VARIABLE + NO + MACOSX_DEPLOYMENT_TARGET + 10.8 + OTHER_CPLUSPLUSFLAGS + + -D__MACOSX_CORE__ + -mtune=native + + SDKROOT + macosx + + isa + XCBuildConfiguration + name + Release + + E4B69B580A3A1756003C02F2 + + buildActionMask + 2147483647 + files + + E4B69E200A3A1BDC003C02F2 + E4B69E210A3A1BDC003C02F2 + 3F59654CA5CEEFA57C8D09DC + BF438D1989EA8474A24FC372 + 51A4134BF889D553064DCD2E + + isa + PBXSourcesBuildPhase + runOnlyForDeploymentPostprocessing + 0 + + E4B69B590A3A1756003C02F2 + + buildActionMask + 2147483647 + files + + E4328149138ABC9F0047C5CB + + isa + PBXFrameworksBuildPhase + runOnlyForDeploymentPostprocessing + 0 + + E4B69B5A0A3A1756003C02F2 + + buildConfigurationList + E4B69B5F0A3A1757003C02F2 + buildPhases + + E4B69B580A3A1756003C02F2 + E4B69B590A3A1756003C02F2 + E4B6FFFD0C3F9AB9008CF71C + E4C2427710CC5ABF004149E2 + + buildRules + + dependencies + + E4EEB9AC138B136A00A80321 + + isa + PBXNativeTarget + name + example + productName + myOFApp + productReference + E4B69B5B0A3A1756003C02F2 + productType + com.apple.product-type.application + + E4B69B5B0A3A1756003C02F2 + + explicitFileType + wrapper.application + includeInIndex + 0 + isa + PBXFileReference + path + exampleDebug.app + sourceTree + BUILT_PRODUCTS_DIR + + E4B69B5F0A3A1757003C02F2 + + buildConfigurations + + E4B69B600A3A1757003C02F2 + E4B69B610A3A1757003C02F2 + + defaultConfigurationIsVisible + 0 + defaultConfigurationName + Release + isa + XCConfigurationList + + E4B69B600A3A1757003C02F2 + + baseConfigurationReference + E4EB6923138AFD0F00A09F29 + buildSettings + + HEADER_SEARCH_PATHS + + $(OF_CORE_HEADERS) + ../../../addons/ofxTSNE/src + ../../../addons/ofxTSNE/src/bhtsne + + COMBINE_HIDPI_IMAGES + YES + COPY_PHASE_STRIP + NO + FRAMEWORK_SEARCH_PATHS + + $(inherited) + $(FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_1) + + FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_1 + "$(SRCROOT)/../../../libs/glut/lib/osx" + GCC_DYNAMIC_NO_PIC + NO + GCC_GENERATE_DEBUGGING_SYMBOLS + YES + GCC_MODEL_TUNING + NONE + ICON + $(ICON_NAME_DEBUG) + ICON_FILE + $(ICON_FILE_PATH)$(ICON) + INFOPLIST_FILE + openFrameworks-Info.plist + INSTALL_PATH + $(HOME)/Applications + LIBRARY_SEARCH_PATHS + $(inherited) + PRODUCT_NAME + $(TARGET_NAME)Debug + WRAPPER_EXTENSION + app + + isa + XCBuildConfiguration + name + Debug + + E4B69B610A3A1757003C02F2 + + baseConfigurationReference + E4EB6923138AFD0F00A09F29 + buildSettings + + HEADER_SEARCH_PATHS + + $(OF_CORE_HEADERS) + ../../../addons/ofxTSNE/src + ../../../addons/ofxTSNE/src/bhtsne + + COMBINE_HIDPI_IMAGES + YES + COPY_PHASE_STRIP + YES + FRAMEWORK_SEARCH_PATHS + + $(inherited) + $(FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_1) + + FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_1 + "$(SRCROOT)/../../../libs/glut/lib/osx" + GCC_GENERATE_DEBUGGING_SYMBOLS + YES + GCC_MODEL_TUNING + NONE + ICON + $(ICON_NAME_RELEASE) + ICON_FILE + $(ICON_FILE_PATH)$(ICON) + INFOPLIST_FILE + openFrameworks-Info.plist + INSTALL_PATH + $(HOME)/Applications + LIBRARY_SEARCH_PATHS + $(inherited) + PRODUCT_NAME + $(TARGET_NAME) + WRAPPER_EXTENSION + app + baseConfigurationReference + E4EB6923138AFD0F00A09F29 + + isa + XCBuildConfiguration + name + Release + + E4B69E1C0A3A1BDC003C02F2 + + children + + E4B69E1D0A3A1BDC003C02F2 + E4B69E1E0A3A1BDC003C02F2 + E4B69E1F0A3A1BDC003C02F2 + + isa + PBXGroup + path + src + sourceTree + SOURCE_ROOT + + E4B69E1D0A3A1BDC003C02F2 + + fileEncoding + 30 + isa + PBXFileReference + lastKnownFileType + sourcecode.cpp.cpp + name + main.cpp + path + src/main.cpp + sourceTree + SOURCE_ROOT + + E4B69E1E0A3A1BDC003C02F2 + + explicitFileType + sourcecode.cpp.cpp + fileEncoding + 30 + isa + PBXFileReference + name + ofApp.cpp + path + src/ofApp.cpp + sourceTree + SOURCE_ROOT + + E4B69E1F0A3A1BDC003C02F2 + + fileEncoding + 30 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + ofApp.h + path + src/ofApp.h + sourceTree + SOURCE_ROOT + + E4B69E200A3A1BDC003C02F2 + + fileRef + E4B69E1D0A3A1BDC003C02F2 + isa + PBXBuildFile + + E4B69E210A3A1BDC003C02F2 + + fileRef + E4B69E1E0A3A1BDC003C02F2 + isa + PBXBuildFile + + E4B6FCAD0C3E899E008CF71C + + fileEncoding + 30 + isa + PBXFileReference + lastKnownFileType + text.plist.xml + path + openFrameworks-Info.plist + sourceTree + <group> + + E4B6FFFD0C3F9AB9008CF71C + + buildActionMask + 2147483647 + files + + inputPaths + + isa + PBXShellScriptBuildPhase + outputPaths + + runOnlyForDeploymentPostprocessing + 0 + shellPath + /bin/sh + shellScript + rsync -aved ../../../libs/fmodex/lib/osx/libfmodex.dylib "$TARGET_BUILD_DIR/$PRODUCT_NAME.app/Contents/MacOS/"; install_name_tool -change ./libfmodex.dylib @executable_path/libfmodex.dylib "$TARGET_BUILD_DIR/$PRODUCT_NAME.app/Contents/MacOS/$PRODUCT_NAME"; +mkdir -p "$TARGET_BUILD_DIR/$PRODUCT_NAME.app/Contents/Resources/" +rsync -aved "$ICON_FILE" "$TARGET_BUILD_DIR/$PRODUCT_NAME.app/Contents/Resources/" +rsync -aved ../../../libs/glut/lib/osx/GLUT.framework "$TARGET_BUILD_DIR/$PRODUCT_NAME.app/Contents/Frameworks/" + + + E4C2427710CC5ABF004149E2 + + buildActionMask + 2147483647 + dstPath + + dstSubfolderSpec + 10 + files + + isa + PBXCopyFilesBuildPhase + runOnlyForDeploymentPostprocessing + 0 + + E4EB691F138AFCF100A09F29 + + fileEncoding + 4 + isa + PBXFileReference + lastKnownFileType + text.xcconfig + name + CoreOF.xcconfig + path + ../../../libs/openFrameworksCompiled/project/osx/CoreOF.xcconfig + sourceTree + SOURCE_ROOT + + E4EB6923138AFD0F00A09F29 + + fileEncoding + 4 + isa + PBXFileReference + lastKnownFileType + text.xcconfig + path + Project.xcconfig + sourceTree + <group> + + E4EEB9AB138B136A00A80321 + + containerPortal + E4328143138ABC890047C5CB + isa + PBXContainerItemProxy + proxyType + 1 + remoteGlobalIDString + E4B27C1410CBEB8E00536013 + remoteInfo + openFrameworks + + E4EEB9AC138B136A00A80321 + + isa + PBXTargetDependency + name + openFrameworks + targetProxy + E4EEB9AB138B136A00A80321 + + E4EEC9E9138DF44700A80321 + + children + + E4EB691F138AFCF100A09F29 + E4328143138ABC890047C5CB + + isa + PBXGroup + name + openFrameworks + sourceTree + <group> + + + rootObject + E4B69B4C0A3A1720003C02F2 + + diff --git a/example/example.xcodeproj/project.xcworkspace/xcuserdata/gene.xcuserdatad/UserInterfaceState.xcuserstate b/example/example.xcodeproj/project.xcworkspace/xcuserdata/gene.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000000000000000000000000000000000000..bf6081a7d28b2d9314d10e79721d01717acd08d4 GIT binary patch literal 20038 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 Y3qCCPgom*Rg8yW?`oLQFYr*ON1C%%h^#A|> literal 0 HcmV?d00001 diff --git a/example/example.xcodeproj/xcshareddata/xcschemes/example Debug.xcscheme b/example/example.xcodeproj/xcshareddata/xcschemes/example Debug.xcscheme new file mode 100644 index 0000000..a637354 --- /dev/null +++ b/example/example.xcodeproj/xcshareddata/xcschemes/example Debug.xcscheme @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/example/example.xcodeproj/xcshareddata/xcschemes/example Release.xcscheme b/example/example.xcodeproj/xcshareddata/xcschemes/example Release.xcscheme new file mode 100644 index 0000000..1b9ed60 --- /dev/null +++ b/example/example.xcodeproj/xcshareddata/xcschemes/example Release.xcscheme @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/example/example.xcodeproj/xcuserdata/gene.xcuserdatad/xcschemes/xcschememanagement.plist b/example/example.xcodeproj/xcuserdata/gene.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..13dfb6e --- /dev/null +++ b/example/example.xcodeproj/xcuserdata/gene.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + SuppressBuildableAutocreation + + E4B69B5A0A3A1756003C02F2 + + primary + + + + + diff --git a/example/openFrameworks-Info.plist b/example/openFrameworks-Info.plist new file mode 100644 index 0000000..8d64d2b --- /dev/null +++ b/example/openFrameworks-Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + cc.openFrameworks.ofapp + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + APPL + CFBundleSignature + ???? + CFBundleVersion + 1.0 + CFBundleIconFile + ${ICON} + + diff --git a/example/src/main.cpp b/example/src/main.cpp new file mode 100644 index 0000000..e57370b --- /dev/null +++ b/example/src/main.cpp @@ -0,0 +1,13 @@ +#include "ofMain.h" +#include "ofApp.h" + +//======================================================================== +int main( ){ + ofSetupOpenGL(1024,768,OF_WINDOW); // <-------- setup the GL context + + // this kicks off the running of my app + // can be OF_WINDOW or OF_FULLSCREEN + // pass in width and height too: + ofRunApp(new ofApp()); + +} diff --git a/example/src/ofApp.cpp b/example/src/ofApp.cpp new file mode 100644 index 0000000..17a0546 --- /dev/null +++ b/example/src/ofApp.cpp @@ -0,0 +1,191 @@ +#include "ofApp.h" + +//-------------------------------------------------------------- +void ofApp::setup(){ + + // first let's construct our toy dataset. + // we will create N samples of dimension D, which will be distributed + // into a number of classes, where a point belonging to a particular + // class will be located close to the center point for that class. + // Note: TSNE doesn't operate on classes/labels and you don't need to + // use them. we are creating the dataset with color-coded classes + // so we can see TSNE's ability to retain clusters of points when + // transforming them from high-dimensional to low-dimensional space, so + // in this example, the classes are just for us to see this clearer. + + // pick initial parameters + int N = 2000; // number of points in our dataset + int D = 100; // number of dimensions in our data + int numClasses = 10; // how many classes to create + + + // first let's make our classes + + vector classColors = vector{ofColor::red, ofColor::green, + ofColor::blue, ofColor::cyan, ofColor::magenta, ofColor::black, + ofColor::yellow, ofColor::violet, ofColor::pink, ofColor::gray}; + vector > classCenters; + classCenters.resize(numClasses); + + for (int i = 0; i < numClasses; i++) { + // pick a random center point for this class + vector classCenter; + classCenter.resize(D); + for (int j=0; j point; + point.resize(D); + + // the TestPoint will be located with some (fairly large) random + // deviation from the center point of its class + for (int j=0; j > where the inner vector corresponds + // to a single data point. So let's unpack our testPoints into this. + + vector > data; + for (int i = 0; i < N; i++) { + data.push_back(testPoints[i].point); + } + + // ofxTSNE takes four parameters: + // + // dims = number of dimensions to embed our points into. we will + // use 2 points to visualize along (x, y), although t-SNE is commonly + // used for 3d as well. since t-SNE is best known for visualization, + // it's most commonly run with 2 or 3 dims, but more can be used. + // perplexity = a measure of the dataset's Shannon entropy, this can be + // loosely interpreted as how uncertain the data is / how difficult it is + // to be modeled by a probability distribution. generally speaking, larger + // datasets need a higher perplexity, but if your points end up looking + // like a poorly-arranged uniformly-distributed ball, your perplexity is + // too high. this value is usually set between 5 and 50 with 30 being + // a good default. t-SNE is fairly insensitive to this parameter so + // in most cases the default should work fine. + // theta / angle = this parameter controls the accuracy/speed tradeoff + // of the Barnes-Hut simulation used to solve the t-SNE embedding. low + // theta is more accurate but slower, and high theta is faster but + // maybe less optimal solution. it should be between 0 and 1. in practice + // you can usually get away with higher theta (say 0.5) without sacrificing + // noticeable accuracy. + // normalize = this will automatically remap all tsne points to range {0, 1} + // if false, you'll get the original points. + + + int dims = 2; + float perplexity = 30; + float theta = 0.5; + bool normalize = true; + + // finally let's run ofxTSNE! this may take a while depending on your + // data, and it will return a set of embedded points, structured as + // a vector > where the inner vector contains (dims) elements. + // We will unpack these points and assign them back to our testPoints dataset. + + vector > tsnePoints = tsne.run(data, dims, perplexity, theta, normalize); + + // unpack the embedded points back into our testPoints + for (int i=0; i point; + ofPoint tsnePoint; + }; + + ofxTSNE tsne; + vector testPoints; +}; diff --git a/ofxaddons_thumbnail.png b/ofxaddons_thumbnail.png new file mode 100644 index 0000000000000000000000000000000000000000..be3d0cc5599841caa71a2e1182c5f8c17ea2239c GIT binary patch literal 49891 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 literal 0 HcmV?d00001 diff --git a/setup_ccv.sh b/setup_ccv.sh new file mode 100755 index 0000000..ee591fd --- /dev/null +++ b/setup_ccv.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash + +DIR=example-images/bin/data +DB_URL="https://raw.githubusercontent.com/liuliu/ccv/unstable/samples/image-net-2012.sqlite3" +DB_FILE="$DIR/image-net-2012.sqlite3" +WORDS_URL="https://raw.githubusercontent.com/liuliu/ccv/unstable/samples/image-net-2012.words" +WORDS_FILE="$DIR/image-net-2012.words" +mkdir -p $DIR +if [ ! -e $DB_FILE ] ; then + echo "Downloading image-net-2012 network (ccv)" + curl -o $DB_FILE $DB_URL + curl -o $WORDS_FILE $WORDS_URL +fi +echo "Downloaded: image-net-2012 network (ccv)" diff --git a/src/bhtsne/sptree.cpp b/src/bhtsne/sptree.cpp new file mode 100755 index 0000000..10aa4ef --- /dev/null +++ b/src/bhtsne/sptree.cpp @@ -0,0 +1,428 @@ +/* + * + * Copyright (c) 2014, Laurens van der Maaten (Delft University of Technology) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the Delft University of Technology. + * 4. Neither the name of the Delft University of Technology nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY LAURENS VAN DER MAATEN ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL LAURENS VAN DER MAATEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include +#include +#include "sptree.h" + + + +// Constructs cell +Cell::Cell(unsigned int inp_dimension) { + dimension = inp_dimension; + corner = (double*) malloc(dimension * sizeof(double)); + width = (double*) malloc(dimension * sizeof(double)); +} + +Cell::Cell(unsigned int inp_dimension, double* inp_corner, double* inp_width) { + dimension = inp_dimension; + corner = (double*) malloc(dimension * sizeof(double)); + width = (double*) malloc(dimension * sizeof(double)); + for(int d = 0; d < dimension; d++) setCorner(d, inp_corner[d]); + for(int d = 0; d < dimension; d++) setWidth( d, inp_width[d]); +} + +// Destructs cell +Cell::~Cell() { + free(corner); + free(width); +} + +double Cell::getCorner(unsigned int d) { + return corner[d]; +} + +double Cell::getWidth(unsigned int d) { + return width[d]; +} + +void Cell::setCorner(unsigned int d, double val) { + corner[d] = val; +} + +void Cell::setWidth(unsigned int d, double val) { + width[d] = val; +} + +// Checks whether a point lies in a cell +bool Cell::containsPoint(double point[]) +{ + for(int d = 0; d < dimension; d++) { + if(corner[d] - width[d] > point[d]) return false; + if(corner[d] + width[d] < point[d]) return false; + } + return true; +} + + +// Default constructor for SPTree -- build tree, too! +SPTree::SPTree(unsigned int D, double* inp_data, unsigned int N) +{ + + // Compute mean, width, and height of current map (boundaries of SPTree) + int nD = 0; + double* mean_Y = (double*) calloc(D, sizeof(double)); + double* min_Y = (double*) malloc(D * sizeof(double)); for(unsigned int d = 0; d < D; d++) min_Y[d] = DBL_MAX; + double* max_Y = (double*) malloc(D * sizeof(double)); for(unsigned int d = 0; d < D; d++) max_Y[d] = -DBL_MAX; + for(unsigned int n = 0; n < N; n++) { + for(unsigned int d = 0; d < D; d++) { + mean_Y[d] += inp_data[n * D + d]; + if(inp_data[nD + d] < min_Y[d]) min_Y[d] = inp_data[nD + d]; + if(inp_data[nD + d] > max_Y[d]) max_Y[d] = inp_data[nD + d]; + } + nD += D; + } + for(int d = 0; d < D; d++) mean_Y[d] /= (double) N; + + // Construct SPTree + double* width = (double*) malloc(D * sizeof(double)); + for(int d = 0; d < D; d++) width[d] = fmax(max_Y[d] - mean_Y[d], mean_Y[d] - min_Y[d]) + 1e-5; + init(NULL, D, inp_data, mean_Y, width); + fill(N); + + // Clean up memory + free(mean_Y); + free(max_Y); + free(min_Y); + free(width); +} + + +// Constructor for SPTree with particular size and parent -- build the tree, too! +SPTree::SPTree(unsigned int D, double* inp_data, unsigned int N, double* inp_corner, double* inp_width) +{ + init(NULL, D, inp_data, inp_corner, inp_width); + fill(N); +} + + +// Constructor for SPTree with particular size (do not fill the tree) +SPTree::SPTree(unsigned int D, double* inp_data, double* inp_corner, double* inp_width) +{ + init(NULL, D, inp_data, inp_corner, inp_width); +} + + +// Constructor for SPTree with particular size and parent (do not fill tree) +SPTree::SPTree(SPTree* inp_parent, unsigned int D, double* inp_data, double* inp_corner, double* inp_width) { + init(inp_parent, D, inp_data, inp_corner, inp_width); +} + + +// Constructor for SPTree with particular size and parent -- build the tree, too! +SPTree::SPTree(SPTree* inp_parent, unsigned int D, double* inp_data, unsigned int N, double* inp_corner, double* inp_width) +{ + init(inp_parent, D, inp_data, inp_corner, inp_width); + fill(N); +} + + +// Main initialization function +void SPTree::init(SPTree* inp_parent, unsigned int D, double* inp_data, double* inp_corner, double* inp_width) +{ + parent = inp_parent; + dimension = D; + no_children = 2; + for(unsigned int d = 1; d < D; d++) no_children *= 2; + data = inp_data; + is_leaf = true; + size = 0; + cum_size = 0; + + boundary = new Cell(dimension); + for(unsigned int d = 0; d < D; d++) boundary->setCorner(d, inp_corner[d]); + for(unsigned int d = 0; d < D; d++) boundary->setWidth( d, inp_width[d]); + + children = (SPTree**) malloc(no_children * sizeof(SPTree*)); + for(unsigned int i = 0; i < no_children; i++) children[i] = NULL; + + center_of_mass = (double*) malloc(D * sizeof(double)); + for(unsigned int d = 0; d < D; d++) center_of_mass[d] = .0; + + buff = (double*) malloc(D * sizeof(double)); +} + + +// Destructor for SPTree +SPTree::~SPTree() +{ + for(unsigned int i = 0; i < no_children; i++) { + if(children[i] != NULL) delete children[i]; + } + free(children); + free(center_of_mass); + free(buff); + delete boundary; +} + + +// Update the data underlying this tree +void SPTree::setData(double* inp_data) +{ + data = inp_data; +} + + +// Get the parent of the current tree +SPTree* SPTree::getParent() +{ + return parent; +} + + +// Insert a point into the SPTree +bool SPTree::insert(unsigned int new_index) +{ + // Ignore objects which do not belong in this quad tree + double* point = data + new_index * dimension; + if(!boundary->containsPoint(point)) + return false; + + // Online update of cumulative size and center-of-mass + cum_size++; + double mult1 = (double) (cum_size - 1) / (double) cum_size; + double mult2 = 1.0 / (double) cum_size; + for(unsigned int d = 0; d < dimension; d++) center_of_mass[d] *= mult1; + for(unsigned int d = 0; d < dimension; d++) center_of_mass[d] += mult2 * point[d]; + + // If there is space in this quad tree and it is a leaf, add the object here + if(is_leaf && size < QT_NODE_CAPACITY) { + index[size] = new_index; + size++; + return true; + } + + // Don't add duplicates for now (this is not very nice) + bool any_duplicate = false; + for(unsigned int n = 0; n < size; n++) { + bool duplicate = true; + for(unsigned int d = 0; d < dimension; d++) { + if(point[d] != data[index[n] * dimension + d]) { duplicate = false; break; } + } + any_duplicate = any_duplicate | duplicate; + } + if(any_duplicate) return true; + + // Otherwise, we need to subdivide the current cell + if(is_leaf) subdivide(); + + // Find out where the point can be inserted + for(unsigned int i = 0; i < no_children; i++) { + if(children[i]->insert(new_index)) return true; + } + + // Otherwise, the point cannot be inserted (this should never happen) + return false; +} + + +// Create four children which fully divide this cell into four quads of equal area +void SPTree::subdivide() { + + // Create new children + double* new_corner = (double*) malloc(dimension * sizeof(double)); + double* new_width = (double*) malloc(dimension * sizeof(double)); + for(unsigned int i = 0; i < no_children; i++) { + unsigned int div = 1; + for(unsigned int d = 0; d < dimension; d++) { + new_width[d] = .5 * boundary->getWidth(d); + if((i / div) % 2 == 1) new_corner[d] = boundary->getCorner(d) - .5 * boundary->getWidth(d); + else new_corner[d] = boundary->getCorner(d) + .5 * boundary->getWidth(d); + div *= 2; + } + children[i] = new SPTree(this, dimension, data, new_corner, new_width); + } + free(new_corner); + free(new_width); + + // Move existing points to correct children + for(unsigned int i = 0; i < size; i++) { + bool success = false; + for(unsigned int j = 0; j < no_children; j++) { + if(!success) success = children[j]->insert(index[i]); + } + index[i] = -1; + } + + // Empty parent node + size = 0; + is_leaf = false; +} + + +// Build SPTree on dataset +void SPTree::fill(unsigned int N) +{ + for(unsigned int i = 0; i < N; i++) insert(i); +} + + +// Checks whether the specified tree is correct +bool SPTree::isCorrect() +{ + for(unsigned int n = 0; n < size; n++) { + double* point = data + index[n] * dimension; + if(!boundary->containsPoint(point)) return false; + } + if(!is_leaf) { + bool correct = true; + for(int i = 0; i < no_children; i++) correct = correct && children[i]->isCorrect(); + return correct; + } + else return true; +} + + + +// Build a list of all indices in SPTree +void SPTree::getAllIndices(unsigned int* indices) +{ + getAllIndices(indices, 0); +} + + +// Build a list of all indices in SPTree +unsigned int SPTree::getAllIndices(unsigned int* indices, unsigned int loc) +{ + + // Gather indices in current quadrant + for(unsigned int i = 0; i < size; i++) indices[loc + i] = index[i]; + loc += size; + + // Gather indices in children + if(!is_leaf) { + for(int i = 0; i < no_children; i++) loc = children[i]->getAllIndices(indices, loc); + } + return loc; +} + + +unsigned int SPTree::getDepth() { + if(is_leaf) return 1; + int depth = 0; + for(unsigned int i = 0; i < no_children; i++) depth = fmax(depth, children[i]->getDepth()); + return 1 + depth; +} + + +// Compute non-edge forces using Barnes-Hut algorithm +void SPTree::computeNonEdgeForces(unsigned int point_index, double theta, double neg_f[], double* sum_Q) +{ + + // Make sure that we spend no time on empty nodes or self-interactions + if(cum_size == 0 || (is_leaf && size == 1 && index[0] == point_index)) return; + + // Compute distance between point and center-of-mass + double D = .0; + unsigned int ind = point_index * dimension; + for(unsigned int d = 0; d < dimension; d++) buff[d] = data[ind + d] - center_of_mass[d]; + for(unsigned int d = 0; d < dimension; d++) D += buff[d] * buff[d]; + + // Check whether we can use this node as a "summary" + double max_width = 0.0; + double cur_width; + for(unsigned int d = 0; d < dimension; d++) { + cur_width = boundary->getWidth(d); + max_width = (max_width > cur_width) ? max_width : cur_width; + } + if(is_leaf || max_width / sqrt(D) < theta) { + + // Compute and add t-SNE force between point and current node + D = 1.0 / (1.0 + D); + double mult = cum_size * D; + *sum_Q += mult; + mult *= D; + for(unsigned int d = 0; d < dimension; d++) neg_f[d] += mult * buff[d]; + } + else { + + // Recursively apply Barnes-Hut to children + for(unsigned int i = 0; i < no_children; i++) children[i]->computeNonEdgeForces(point_index, theta, neg_f, sum_Q); + } +} + + +// Computes edge forces +void SPTree::computeEdgeForces(unsigned int* row_P, unsigned int* col_P, double* val_P, int N, double* pos_f) +{ + + // Loop over all edges in the graph + unsigned int ind1 = 0; + unsigned int ind2 = 0; + double D; + for(unsigned int n = 0; n < N; n++) { + for(unsigned int i = row_P[n]; i < row_P[n + 1]; i++) { + + // Compute pairwise distance and Q-value + D = 1.0; + ind2 = col_P[i] * dimension; + for(unsigned int d = 0; d < dimension; d++) buff[d] = data[ind1 + d] - data[ind2 + d]; + for(unsigned int d = 0; d < dimension; d++) D += buff[d] * buff[d]; + D = val_P[i] / D; + + // Sum positive force + for(unsigned int d = 0; d < dimension; d++) pos_f[ind1 + d] += D * buff[d]; + } + ind1 += dimension; + } +} + + +// Print out tree +void SPTree::print() +{ + if(cum_size == 0) { + printf("Empty node\n"); + return; + } + + if(is_leaf) { + printf("Leaf node; data = ["); + for(int i = 0; i < size; i++) { + double* point = data + index[i] * dimension; + for(int d = 0; d < dimension; d++) printf("%f, ", point[d]); + printf(" (index = %d)", index[i]); + if(i < size - 1) printf("\n"); + else printf("]\n"); + } + } + else { + printf("Intersection node with center-of-mass = ["); + for(int d = 0; d < dimension; d++) printf("%f, ", center_of_mass[d]); + printf("]; children are:\n"); + for(int i = 0; i < no_children; i++) children[i]->print(); + } +} + diff --git a/src/bhtsne/sptree.h b/src/bhtsne/sptree.h new file mode 100755 index 0000000..b138a9a --- /dev/null +++ b/src/bhtsne/sptree.h @@ -0,0 +1,115 @@ +/* + * + * Copyright (c) 2014, Laurens van der Maaten (Delft University of Technology) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the Delft University of Technology. + * 4. Neither the name of the Delft University of Technology nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY LAURENS VAN DER MAATEN ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL LAURENS VAN DER MAATEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + */ + + +#ifndef SPTREE_H +#define SPTREE_H + +using namespace std; + + +class Cell { + + unsigned int dimension; + double* corner; + double* width; + + +public: + Cell(unsigned int inp_dimension); + Cell(unsigned int inp_dimension, double* inp_corner, double* inp_width); + ~Cell(); + + double getCorner(unsigned int d); + double getWidth(unsigned int d); + void setCorner(unsigned int d, double val); + void setWidth(unsigned int d, double val); + bool containsPoint(double point[]); +}; + + +class SPTree +{ + + // Fixed constants + static const unsigned int QT_NODE_CAPACITY = 1; + + // A buffer we use when doing force computations + double* buff; + + // Properties of this node in the tree + SPTree* parent; + unsigned int dimension; + bool is_leaf; + unsigned int size; + unsigned int cum_size; + + // Axis-aligned bounding box stored as a center with half-dimensions to represent the boundaries of this quad tree + Cell* boundary; + + // Indices in this space-partitioning tree node, corresponding center-of-mass, and list of all children + double* data; + double* center_of_mass; + unsigned int index[QT_NODE_CAPACITY]; + + // Children + SPTree** children; + unsigned int no_children; + +public: + SPTree(unsigned int D, double* inp_data, unsigned int N); + SPTree(unsigned int D, double* inp_data, double* inp_corner, double* inp_width); + SPTree(unsigned int D, double* inp_data, unsigned int N, double* inp_corner, double* inp_width); + SPTree(SPTree* inp_parent, unsigned int D, double* inp_data, unsigned int N, double* inp_corner, double* inp_width); + SPTree(SPTree* inp_parent, unsigned int D, double* inp_data, double* inp_corner, double* inp_width); + ~SPTree(); + void setData(double* inp_data); + SPTree* getParent(); + void construct(Cell boundary); + bool insert(unsigned int new_index); + void subdivide(); + bool isCorrect(); + void rebuildTree(); + void getAllIndices(unsigned int* indices); + unsigned int getDepth(); + void computeNonEdgeForces(unsigned int point_index, double theta, double neg_f[], double* sum_Q); + void computeEdgeForces(unsigned int* row_P, unsigned int* col_P, double* val_P, int N, double* pos_f); + void print(); + +private: + void init(SPTree* inp_parent, unsigned int D, double* inp_data, double* inp_corner, double* inp_width); + void fill(unsigned int N); + unsigned int getAllIndices(unsigned int* indices, unsigned int loc); + bool isChild(unsigned int test_index, unsigned int start, unsigned int end); +}; + +#endif diff --git a/src/bhtsne/tsne.cpp b/src/bhtsne/tsne.cpp new file mode 100755 index 0000000..11fa0a4 --- /dev/null +++ b/src/bhtsne/tsne.cpp @@ -0,0 +1,758 @@ +/* + * + * Copyright (c) 2014, Laurens van der Maaten (Delft University of Technology) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the Delft University of Technology. + * 4. Neither the name of the Delft University of Technology nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY LAURENS VAN DER MAATEN ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL LAURENS VAN DER MAATEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + */ + + + +#include +#include +#include +#include +#include +#include +#include "vptree.h" +#include "sptree.h" +#include "tsne.h" + + +using namespace std; + +// Perform t-SNE +void TSNE::run(double* X, int N, int D, double* Y, int no_dims, double perplexity, double theta) { + + // Determine whether we are using an exact algorithm + if(N - 1 < 3 * perplexity) { printf("Perplexity too large for the number of data points!\n"); exit(1); } + printf("Using no_dims = %d, perplexity = %f, and theta = %f\n", no_dims, perplexity, theta); + bool exact = (theta == .0) ? true : false; + + // Set learning parameters + float total_time = .0; + clock_t start, end; + int max_iter = 1000, stop_lying_iter = 250, mom_switch_iter = 250; + double momentum = .5, final_momentum = .8; + double eta = 200.0; + + // Allocate some memory + double* dY = (double*) malloc(N * no_dims * sizeof(double)); + double* uY = (double*) malloc(N * no_dims * sizeof(double)); + double* gains = (double*) malloc(N * no_dims * sizeof(double)); + if(dY == NULL || uY == NULL || gains == NULL) { printf("Memory allocation failed!\n"); exit(1); } + for(int i = 0; i < N * no_dims; i++) uY[i] = .0; + for(int i = 0; i < N * no_dims; i++) gains[i] = 1.0; + + // Normalize input data (to prevent numerical problems) + printf("Computing input similarities...\n"); + start = clock(); + zeroMean(X, N, D); + double max_X = .0; + for(int i = 0; i < N * D; i++) { + if(X[i] > max_X) max_X = X[i]; + } + for(int i = 0; i < N * D; i++) X[i] /= max_X; + + // Compute input similarities for exact t-SNE + double* P; unsigned int* row_P; unsigned int* col_P; double* val_P; + if(exact) { + + // Compute similarities + printf("Exact?"); + P = (double*) malloc(N * N * sizeof(double)); + if(P == NULL) { printf("Memory allocation failed!\n"); exit(1); } + computeGaussianPerplexity(X, N, D, P, perplexity); + + // Symmetrize input similarities + printf("Symmetrizing...\n"); + int nN = 0; + for(int n = 0; n < N; n++) { + int mN = 0; + for(int m = n + 1; m < N; m++) { + P[nN + m] += P[mN + n]; + P[mN + n] = P[nN + m]; + mN += N; + } + nN += N; + } + double sum_P = .0; + for(int i = 0; i < N * N; i++) sum_P += P[i]; + for(int i = 0; i < N * N; i++) P[i] /= sum_P; + } + + // Compute input similarities for approximate t-SNE + else { + + // Compute asymmetric pairwise input similarities + computeGaussianPerplexity(X, N, D, &row_P, &col_P, &val_P, perplexity, (int) (3 * perplexity)); + + // Symmetrize input similarities + symmetrizeMatrix(&row_P, &col_P, &val_P, N); + double sum_P = .0; + for(int i = 0; i < row_P[N]; i++) sum_P += val_P[i]; + for(int i = 0; i < row_P[N]; i++) val_P[i] /= sum_P; + } + end = clock(); + + // Lie about the P-values + if(exact) { for(int i = 0; i < N * N; i++) P[i] *= 12.0; } + else { for(int i = 0; i < row_P[N]; i++) val_P[i] *= 12.0; } + + // Initialize solution (randomly) + for(int i = 0; i < N * no_dims; i++) Y[i] = randn() * .0001; + + // Perform main training loop + if(exact) printf("Input similarities computed in %4.2f seconds!\nLearning embedding...\n", (float) (end - start) / CLOCKS_PER_SEC); + else printf("Input similarities computed in %4.2f seconds (sparsity = %f)!\nLearning embedding...\n", (float) (end - start) / CLOCKS_PER_SEC, (double) row_P[N] / ((double) N * (double) N)); + start = clock(); + for(int iter = 0; iter < max_iter; iter++) { + + // Compute (approximate) gradient + if(exact) computeExactGradient(P, Y, N, no_dims, dY); + else computeGradient(P, row_P, col_P, val_P, Y, N, no_dims, dY, theta); + + // Update gains + for(int i = 0; i < N * no_dims; i++) gains[i] = (sign(dY[i]) != sign(uY[i])) ? (gains[i] + .2) : (gains[i] * .8); + for(int i = 0; i < N * no_dims; i++) if(gains[i] < .01) gains[i] = .01; + + // Perform gradient update (with momentum and gains) + for(int i = 0; i < N * no_dims; i++) uY[i] = momentum * uY[i] - eta * gains[i] * dY[i]; + for(int i = 0; i < N * no_dims; i++) Y[i] = Y[i] + uY[i]; + + // Make solution zero-mean + zeroMean(Y, N, no_dims); + + // Stop lying about the P-values after a while, and switch momentum + if(iter == stop_lying_iter) { + if(exact) { for(int i = 0; i < N * N; i++) P[i] /= 12.0; } + else { for(int i = 0; i < row_P[N]; i++) val_P[i] /= 12.0; } + } + if(iter == mom_switch_iter) momentum = final_momentum; + + // Print out progress + if(iter > 0 && (iter % 50 == 0 || iter == max_iter - 1)) { + end = clock(); + double C = .0; + if(exact) C = evaluateError(P, Y, N, no_dims); + else C = evaluateError(row_P, col_P, val_P, Y, N, no_dims, theta); // doing approximate computation here! + if(iter == 0) + printf("Iteration %d: error is %f\n", iter + 1, C); + else { + total_time += (float) (end - start) / CLOCKS_PER_SEC; + printf("Iteration %d: error is %f (50 iterations in %4.2f seconds)\n", iter, C, (float) (end - start) / CLOCKS_PER_SEC); + } + start = clock(); + } + } + end = clock(); total_time += (float) (end - start) / CLOCKS_PER_SEC; + + // Clean up memory + free(dY); + free(uY); + free(gains); + if(exact) free(P); + else { + free(row_P); row_P = NULL; + free(col_P); col_P = NULL; + free(val_P); val_P = NULL; + } + printf("Fitting performed in %4.2f seconds.\n", total_time); +} + + +// Compute gradient of the t-SNE cost function (using Barnes-Hut algorithm) +void TSNE::computeGradient(double* P, unsigned int* inp_row_P, unsigned int* inp_col_P, double* inp_val_P, double* Y, int N, int D, double* dC, double theta) +{ + + // Construct space-partitioning tree on current map + SPTree* tree = new SPTree(D, Y, N); + + // Compute all terms required for t-SNE gradient + double sum_Q = .0; + double* pos_f = (double*) calloc(N * D, sizeof(double)); + double* neg_f = (double*) calloc(N * D, sizeof(double)); + if(pos_f == NULL || neg_f == NULL) { printf("Memory allocation failed!\n"); exit(1); } + tree->computeEdgeForces(inp_row_P, inp_col_P, inp_val_P, N, pos_f); + for(int n = 0; n < N; n++) tree->computeNonEdgeForces(n, theta, neg_f + n * D, &sum_Q); + + // Compute final t-SNE gradient + for(int i = 0; i < N * D; i++) { + dC[i] = pos_f[i] - (neg_f[i] / sum_Q); + } + free(pos_f); + free(neg_f); + delete tree; +} + +// Compute gradient of the t-SNE cost function (exact) +void TSNE::computeExactGradient(double* P, double* Y, int N, int D, double* dC) { + + // Make sure the current gradient contains zeros + for(int i = 0; i < N * D; i++) dC[i] = 0.0; + + // Compute the squared Euclidean distance matrix + double* DD = (double*) malloc(N * N * sizeof(double)); + if(DD == NULL) { printf("Memory allocation failed!\n"); exit(1); } + computeSquaredEuclideanDistance(Y, N, D, DD); + + // Compute Q-matrix and normalization sum + double* Q = (double*) malloc(N * N * sizeof(double)); + if(Q == NULL) { printf("Memory allocation failed!\n"); exit(1); } + double sum_Q = .0; + int nN = 0; + for(int n = 0; n < N; n++) { + for(int m = 0; m < N; m++) { + if(n != m) { + Q[nN + m] = 1 / (1 + DD[nN + m]); + sum_Q += Q[nN + m]; + } + } + nN += N; + } + + // Perform the computation of the gradient + nN = 0; + int nD = 0; + for(int n = 0; n < N; n++) { + int mD = 0; + for(int m = 0; m < N; m++) { + if(n != m) { + double mult = (P[nN + m] - (Q[nN + m] / sum_Q)) * Q[nN + m]; + for(int d = 0; d < D; d++) { + dC[nD + d] += (Y[nD + d] - Y[mD + d]) * mult; + } + } + mD += D; + } + nN += N; + nD += D; + } + + // Free memory + free(DD); DD = NULL; + free(Q); Q = NULL; +} + + +// Evaluate t-SNE cost function (exactly) +double TSNE::evaluateError(double* P, double* Y, int N, int D) { + + // Compute the squared Euclidean distance matrix + double* DD = (double*) malloc(N * N * sizeof(double)); + double* Q = (double*) malloc(N * N * sizeof(double)); + if(DD == NULL || Q == NULL) { printf("Memory allocation failed!\n"); exit(1); } + computeSquaredEuclideanDistance(Y, N, D, DD); + + // Compute Q-matrix and normalization sum + int nN = 0; + double sum_Q = DBL_MIN; + for(int n = 0; n < N; n++) { + for(int m = 0; m < N; m++) { + if(n != m) { + Q[nN + m] = 1 / (1 + DD[nN + m]); + sum_Q += Q[nN + m]; + } + else Q[nN + m] = DBL_MIN; + } + nN += N; + } + for(int i = 0; i < N * N; i++) Q[i] /= sum_Q; + + // Sum t-SNE error + double C = .0; + for(int n = 0; n < N * N; n++) { + C += P[n] * log((P[n] + FLT_MIN) / (Q[n] + FLT_MIN)); + } + + // Clean up memory + free(DD); + free(Q); + return C; +} + +// Evaluate t-SNE cost function (approximately) +double TSNE::evaluateError(unsigned int* row_P, unsigned int* col_P, double* val_P, double* Y, int N, int D, double theta) +{ + + // Get estimate of normalization term + SPTree* tree = new SPTree(D, Y, N); + double* buff = (double*) calloc(D, sizeof(double)); + double sum_Q = .0; + for(int n = 0; n < N; n++) tree->computeNonEdgeForces(n, theta, buff, &sum_Q); + + // Loop over all edges to compute t-SNE error + int ind1, ind2; + double C = .0, Q; + for(int n = 0; n < N; n++) { + ind1 = n * D; + for(int i = row_P[n]; i < row_P[n + 1]; i++) { + Q = .0; + ind2 = col_P[i] * D; + for(int d = 0; d < D; d++) buff[d] = Y[ind1 + d]; + for(int d = 0; d < D; d++) buff[d] -= Y[ind2 + d]; + for(int d = 0; d < D; d++) Q += buff[d] * buff[d]; + Q = (1.0 / (1.0 + Q)) / sum_Q; + C += val_P[i] * log((val_P[i] + FLT_MIN) / (Q + FLT_MIN)); + } + } + + // Clean up memory + free(buff); + delete tree; + return C; +} + + +// Compute input similarities with a fixed perplexity +void TSNE::computeGaussianPerplexity(double* X, int N, int D, double* P, double perplexity) { + + // Compute the squared Euclidean distance matrix + double* DD = (double*) malloc(N * N * sizeof(double)); + if(DD == NULL) { printf("Memory allocation failed!\n"); exit(1); } + computeSquaredEuclideanDistance(X, N, D, DD); + + // Compute the Gaussian kernel row by row + int nN = 0; + for(int n = 0; n < N; n++) { + + // Initialize some variables + bool found = false; + double beta = 1.0; + double min_beta = -DBL_MAX; + double max_beta = DBL_MAX; + double tol = 1e-5; + double sum_P; + + // Iterate until we found a good perplexity + int iter = 0; + while(!found && iter < 200) { + + // Compute Gaussian kernel row + for(int m = 0; m < N; m++) P[nN + m] = exp(-beta * DD[nN + m]); + P[nN + n] = DBL_MIN; + + // Compute entropy of current row + sum_P = DBL_MIN; + for(int m = 0; m < N; m++) sum_P += P[nN + m]; + double H = 0.0; + for(int m = 0; m < N; m++) H += beta * (DD[nN + m] * P[nN + m]); + H = (H / sum_P) + log(sum_P); + + // Evaluate whether the entropy is within the tolerance level + double Hdiff = H - log(perplexity); + if(Hdiff < tol && -Hdiff < tol) { + found = true; + } + else { + if(Hdiff > 0) { + min_beta = beta; + if(max_beta == DBL_MAX || max_beta == -DBL_MAX) + beta *= 2.0; + else + beta = (beta + max_beta) / 2.0; + } + else { + max_beta = beta; + if(min_beta == -DBL_MAX || min_beta == DBL_MAX) + beta /= 2.0; + else + beta = (beta + min_beta) / 2.0; + } + } + + // Update iteration counter + iter++; + } + + // Row normalize P + for(int m = 0; m < N; m++) P[nN + m] /= sum_P; + nN += N; + } + + // Clean up memory + free(DD); DD = NULL; +} + + +// Compute input similarities with a fixed perplexity using ball trees (this function allocates memory another function should free) +void TSNE::computeGaussianPerplexity(double* X, int N, int D, unsigned int** _row_P, unsigned int** _col_P, double** _val_P, double perplexity, int K) { + + if(perplexity > K) printf("Perplexity should be lower than K!\n"); + + // Allocate the memory we need + *_row_P = (unsigned int*) malloc((N + 1) * sizeof(unsigned int)); + *_col_P = (unsigned int*) calloc(N * K, sizeof(unsigned int)); + *_val_P = (double*) calloc(N * K, sizeof(double)); + if(*_row_P == NULL || *_col_P == NULL || *_val_P == NULL) { printf("Memory allocation failed!\n"); exit(1); } + unsigned int* row_P = *_row_P; + unsigned int* col_P = *_col_P; + double* val_P = *_val_P; + double* cur_P = (double*) malloc((N - 1) * sizeof(double)); + if(cur_P == NULL) { printf("Memory allocation failed!\n"); exit(1); } + row_P[0] = 0; + for(int n = 0; n < N; n++) row_P[n + 1] = row_P[n] + (unsigned int) K; + + // Build ball tree on data set + VpTree* tree = new VpTree(); + vector obj_X(N, DataPoint(D, -1, X)); + for(int n = 0; n < N; n++) obj_X[n] = DataPoint(D, n, X + n * D); + tree->create(obj_X); + + // Loop over all points to find nearest neighbors + printf("Building tree...\n"); + vector indices; + vector distances; + for(int n = 0; n < N; n++) { + + if(n % 10000 == 0) printf(" - point %d of %d\n", n, N); + + // Find nearest neighbors + indices.clear(); + distances.clear(); + tree->search(obj_X[n], K + 1, &indices, &distances); + + // Initialize some variables for binary search + bool found = false; + double beta = 1.0; + double min_beta = -DBL_MAX; + double max_beta = DBL_MAX; + double tol = 1e-5; + + // Iterate until we found a good perplexity + int iter = 0; double sum_P; + while(!found && iter < 200) { + + // Compute Gaussian kernel row + for(int m = 0; m < K; m++) cur_P[m] = exp(-beta * distances[m + 1] * distances[m + 1]); + + // Compute entropy of current row + sum_P = DBL_MIN; + for(int m = 0; m < K; m++) sum_P += cur_P[m]; + double H = .0; + for(int m = 0; m < K; m++) H += beta * (distances[m + 1] * distances[m + 1] * cur_P[m]); + H = (H / sum_P) + log(sum_P); + + // Evaluate whether the entropy is within the tolerance level + double Hdiff = H - log(perplexity); + if(Hdiff < tol && -Hdiff < tol) { + found = true; + } + else { + if(Hdiff > 0) { + min_beta = beta; + if(max_beta == DBL_MAX || max_beta == -DBL_MAX) + beta *= 2.0; + else + beta = (beta + max_beta) / 2.0; + } + else { + max_beta = beta; + if(min_beta == -DBL_MAX || min_beta == DBL_MAX) + beta /= 2.0; + else + beta = (beta + min_beta) / 2.0; + } + } + + // Update iteration counter + iter++; + } + + // Row-normalize current row of P and store in matrix + for(unsigned int m = 0; m < K; m++) cur_P[m] /= sum_P; + for(unsigned int m = 0; m < K; m++) { + col_P[row_P[n] + m] = (unsigned int) indices[m + 1].index(); + val_P[row_P[n] + m] = cur_P[m]; + } + } + + // Clean up memory + obj_X.clear(); + free(cur_P); + delete tree; +} + + +// Symmetrizes a sparse matrix +void TSNE::symmetrizeMatrix(unsigned int** _row_P, unsigned int** _col_P, double** _val_P, int N) { + + // Get sparse matrix + unsigned int* row_P = *_row_P; + unsigned int* col_P = *_col_P; + double* val_P = *_val_P; + + // Count number of elements and row counts of symmetric matrix + int* row_counts = (int*) calloc(N, sizeof(int)); + if(row_counts == NULL) { printf("Memory allocation failed!\n"); exit(1); } + for(int n = 0; n < N; n++) { + for(int i = row_P[n]; i < row_P[n + 1]; i++) { + + // Check whether element (col_P[i], n) is present + bool present = false; + for(int m = row_P[col_P[i]]; m < row_P[col_P[i] + 1]; m++) { + if(col_P[m] == n) present = true; + } + if(present) row_counts[n]++; + else { + row_counts[n]++; + row_counts[col_P[i]]++; + } + } + } + int no_elem = 0; + for(int n = 0; n < N; n++) no_elem += row_counts[n]; + + // Allocate memory for symmetrized matrix + unsigned int* sym_row_P = (unsigned int*) malloc((N + 1) * sizeof(unsigned int)); + unsigned int* sym_col_P = (unsigned int*) malloc(no_elem * sizeof(unsigned int)); + double* sym_val_P = (double*) malloc(no_elem * sizeof(double)); + if(sym_row_P == NULL || sym_col_P == NULL || sym_val_P == NULL) { printf("Memory allocation failed!\n"); exit(1); } + + // Construct new row indices for symmetric matrix + sym_row_P[0] = 0; + for(int n = 0; n < N; n++) sym_row_P[n + 1] = sym_row_P[n] + (unsigned int) row_counts[n]; + + // Fill the result matrix + int* offset = (int*) calloc(N, sizeof(int)); + if(offset == NULL) { printf("Memory allocation failed!\n"); exit(1); } + for(int n = 0; n < N; n++) { + for(unsigned int i = row_P[n]; i < row_P[n + 1]; i++) { // considering element(n, col_P[i]) + + // Check whether element (col_P[i], n) is present + bool present = false; + for(unsigned int m = row_P[col_P[i]]; m < row_P[col_P[i] + 1]; m++) { + if(col_P[m] == n) { + present = true; + if(n <= col_P[i]) { // make sure we do not add elements twice + sym_col_P[sym_row_P[n] + offset[n]] = col_P[i]; + sym_col_P[sym_row_P[col_P[i]] + offset[col_P[i]]] = n; + sym_val_P[sym_row_P[n] + offset[n]] = val_P[i] + val_P[m]; + sym_val_P[sym_row_P[col_P[i]] + offset[col_P[i]]] = val_P[i] + val_P[m]; + } + } + } + + // If (col_P[i], n) is not present, there is no addition involved + if(!present) { + sym_col_P[sym_row_P[n] + offset[n]] = col_P[i]; + sym_col_P[sym_row_P[col_P[i]] + offset[col_P[i]]] = n; + sym_val_P[sym_row_P[n] + offset[n]] = val_P[i]; + sym_val_P[sym_row_P[col_P[i]] + offset[col_P[i]]] = val_P[i]; + } + + // Update offsets + if(!present || (present && n <= col_P[i])) { + offset[n]++; + if(col_P[i] != n) offset[col_P[i]]++; + } + } + } + + // Divide the result by two + for(int i = 0; i < no_elem; i++) sym_val_P[i] /= 2.0; + + // Return symmetrized matrices + free(*_row_P); *_row_P = sym_row_P; + free(*_col_P); *_col_P = sym_col_P; + free(*_val_P); *_val_P = sym_val_P; + + // Free up some memery + free(offset); offset = NULL; + free(row_counts); row_counts = NULL; +} + +// Compute squared Euclidean distance matrix (using BLAS) +void TSNE::computeSquaredEuclideanDistance(double* X, int N, int D, double* DD) { + double* dataSums = (double*) calloc(N, sizeof(double)); + if(dataSums == NULL) { printf("Memory allocation failed!\n"); exit(1); } + int nD = 0; + for(int n = 0; n < N; n++) { + for(int d = 0; d < D; d++) { + dataSums[n] += (X[nD + d] * X[nD + d]); + } + nD += D; + } + int nN = 0; + for(int n = 0; n < N; n++) { + for(int m = 0; m < N; m++) { + DD[nN + m] = dataSums[n] + dataSums[m]; + } + nN += N; + } + nN = 0; nD = 0; + for(int n = 0; n < N; n++) { + int mD = 0; + DD[nN + n] = 0.0; + for(int m = n + 1; m < N; m++) { + DD[nN + m] = 0.0; + for(int d = 0; d < D; d++) { + DD[nN + m] += (X[nD + d] - X[mD + d]) * (X[nD + d] - X[mD + d]); + } + DD[m * N + n] = DD[nN + m]; + mD += D; + } + nN += N; nD += D; + } + free(dataSums); dataSums = NULL; +} + + +// Makes data zero-mean +void TSNE::zeroMean(double* X, int N, int D) { + + // Compute data mean + double* mean = (double*) calloc(D, sizeof(double)); + if(mean == NULL) { printf("Memory allocation failed!\n"); exit(1); } + int nD = 0; + for(int n = 0; n < N; n++) { + for(int d = 0; d < D; d++) { + mean[d] += X[nD + d]; + } + nD += D; + } + for(int d = 0; d < D; d++) { + mean[d] /= (double) N; + } + + // Subtract data mean + nD = 0; + for(int n = 0; n < N; n++) { + for(int d = 0; d < D; d++) { + X[nD + d] -= mean[d]; + } + nD += D; + } + free(mean); mean = NULL; +} + + +// Generates a Gaussian random number +double TSNE::randn() { + double x, y, radius; + do { + x = 2 * (rand() / ((double) RAND_MAX + 1)) - 1; + y = 2 * (rand() / ((double) RAND_MAX + 1)) - 1; + radius = (x * x) + (y * y); + } while((radius >= 1.0) || (radius == 0.0)); + radius = sqrt(-2 * log(radius) / radius); + x *= radius; + y *= radius; + return x; +} + +// Function that loads data from a t-SNE file +// Note: this function does a malloc that should be freed elsewhere +bool TSNE::load_data(double** data, int* n, int* d, int* no_dims, double* theta, double* perplexity, int* rand_seed) { + + // Open file, read first 2 integers, allocate memory, and read the data + FILE *h; + if((h = fopen("data.dat", "r+b")) == NULL) { + printf("Error: could not open data file.\n"); + return false; + } + fread(n, sizeof(int), 1, h); // number of datapoints + fread(d, sizeof(int), 1, h); // original dimensionality + fread(theta, sizeof(double), 1, h); // gradient accuracy + fread(perplexity, sizeof(double), 1, h); // perplexity + fread(no_dims, sizeof(int), 1, h); // output dimensionality + *data = (double*) malloc(*d * *n * sizeof(double)); + if(*data == NULL) { printf("Memory allocation failed!\n"); exit(1); } + fread(*data, sizeof(double), *n * *d, h); // the data + if(!feof(h)) fread(rand_seed, sizeof(int), 1, h); // random seed + fclose(h); + printf("Read the %i x %i data matrix successfully!\n", *n, *d); + return true; +} + +// Function that saves map to a t-SNE file +void TSNE::save_data(double* data, int* landmarks, double* costs, int n, int d) { + + // Open file, write first 2 integers and then the data + FILE *h; + if((h = fopen("result.dat", "w+b")) == NULL) { + printf("Error: could not open data file.\n"); + return; + } + fwrite(&n, sizeof(int), 1, h); + fwrite(&d, sizeof(int), 1, h); + fwrite(data, sizeof(double), n * d, h); + fwrite(landmarks, sizeof(int), n, h); + fwrite(costs, sizeof(double), n, h); + fclose(h); + printf("Wrote the %i x %i data matrix successfully!\n", n, d); +} + +/* +// Function that runs the Barnes-Hut implementation of t-SNE +int main() { + + // Define some variables + int origN, N, D, no_dims, *landmarks; + double perc_landmarks; + double perplexity, theta, *data; + int rand_seed = -1; + TSNE* tsne = new TSNE(); + + // Read the parameters and the dataset + if(tsne->load_data(&data, &origN, &D, &no_dims, &theta, &perplexity, &rand_seed)) { + + // Set random seed + if(rand_seed >= 0) { + printf("Using random seed: %d\n", rand_seed); + srand((unsigned int) rand_seed); + } + else { + printf("Using current time as random seed...\n"); + srand(time(NULL)); + } + + // Make dummy landmarks + N = origN; + int* landmarks = (int*) malloc(N * sizeof(int)); + if(landmarks == NULL) { printf("Memory allocation failed!\n"); exit(1); } + for(int n = 0; n < N; n++) landmarks[n] = n; + + // Now fire up the SNE implementation + double* Y = (double*) malloc(N * no_dims * sizeof(double)); + double* costs = (double*) calloc(N, sizeof(double)); + if(Y == NULL || costs == NULL) { printf("Memory allocation failed!\n"); exit(1); } + tsne->run(data, N, D, Y, no_dims, perplexity, theta); + + // Save the results + tsne->save_data(Y, landmarks, costs, N, no_dims); + + // Clean up the memory + free(data); data = NULL; + free(Y); Y = NULL; + free(costs); costs = NULL; + free(landmarks); landmarks = NULL; + } + delete(tsne); +} + +*/ \ No newline at end of file diff --git a/src/bhtsne/tsne.h b/src/bhtsne/tsne.h new file mode 100755 index 0000000..4e6a800 --- /dev/null +++ b/src/bhtsne/tsne.h @@ -0,0 +1,63 @@ +/* + * + * Copyright (c) 2014, Laurens van der Maaten (Delft University of Technology) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the Delft University of Technology. + * 4. Neither the name of the Delft University of Technology nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY LAURENS VAN DER MAATEN ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL LAURENS VAN DER MAATEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + */ + + +#ifndef TSNE_H +#define TSNE_H + + +static inline double sign(double x) { return (x == .0 ? .0 : (x < .0 ? -1.0 : 1.0)); } + + +class TSNE +{ +public: + void run(double* X, int N, int D, double* Y, int no_dims, double perplexity, double theta); + bool load_data(double** data, int* n, int* d, int* no_dims, double* theta, double* perplexity, int* rand_seed); + void save_data(double* data, int* landmarks, double* costs, int n, int d); + void symmetrizeMatrix(unsigned int** row_P, unsigned int** col_P, double** val_P, int N); // should be static! + + +private: + void computeGradient(double* P, unsigned int* inp_row_P, unsigned int* inp_col_P, double* inp_val_P, double* Y, int N, int D, double* dC, double theta); + void computeExactGradient(double* P, double* Y, int N, int D, double* dC); + double evaluateError(double* P, double* Y, int N, int D); + double evaluateError(unsigned int* row_P, unsigned int* col_P, double* val_P, double* Y, int N, int D, double theta); + void zeroMean(double* X, int N, int D); + void computeGaussianPerplexity(double* X, int N, int D, double* P, double perplexity); + void computeGaussianPerplexity(double* X, int N, int D, unsigned int** _row_P, unsigned int** _col_P, double** _val_P, double perplexity, int K); + void computeSquaredEuclideanDistance(double* X, int N, int D, double* DD); + double randn(); +}; + +#endif + diff --git a/src/bhtsne/vptree.h b/src/bhtsne/vptree.h new file mode 100755 index 0000000..9e301d6 --- /dev/null +++ b/src/bhtsne/vptree.h @@ -0,0 +1,272 @@ +/* + * + * Copyright (c) 2014, Laurens van der Maaten (Delft University of Technology) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the Delft University of Technology. + * 4. Neither the name of the Delft University of Technology nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY LAURENS VAN DER MAATEN ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL LAURENS VAN DER MAATEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + */ + + +/* This code was adopted with minor modifications from Steve Hanov's great tutorial at http://stevehanov.ca/blog/index.php?id=130 */ + +#include +#include +#include +#include +#include +#include +#include + + +#ifndef VPTREE_H +#define VPTREE_H + +class DataPoint +{ + int _ind; + +public: + double* _x; + int _D; + DataPoint() { + _D = 1; + _ind = -1; + _x = NULL; + } + DataPoint(int D, int ind, double* x) { + _D = D; + _ind = ind; + _x = (double*) malloc(_D * sizeof(double)); + for(int d = 0; d < _D; d++) _x[d] = x[d]; + } + DataPoint(const DataPoint& other) { // this makes a deep copy -- should not free anything + if(this != &other) { + _D = other.dimensionality(); + _ind = other.index(); + _x = (double*) malloc(_D * sizeof(double)); + for(int d = 0; d < _D; d++) _x[d] = other.x(d); + } + } + ~DataPoint() { if(_x != NULL) free(_x); } + DataPoint& operator= (const DataPoint& other) { // asignment should free old object + if(this != &other) { + if(_x != NULL) free(_x); + _D = other.dimensionality(); + _ind = other.index(); + _x = (double*) malloc(_D * sizeof(double)); + for(int d = 0; d < _D; d++) _x[d] = other.x(d); + } + return *this; + } + int index() const { return _ind; } + int dimensionality() const { return _D; } + double x(int d) const { return _x[d]; } +}; + +double euclidean_distance(const DataPoint &t1, const DataPoint &t2) { + double dd = .0; + double* x1 = t1._x; + double* x2 = t2._x; + double diff; + for(int d = 0; d < t1._D; d++) { + diff = (x1[d] - x2[d]); + dd += diff * diff; + } + return sqrt(dd); +} + + +template +class VpTree +{ +public: + + // Default constructor + VpTree() : _root(0) {} + + // Destructor + ~VpTree() { + delete _root; + } + + // Function to create a new VpTree from data + void create(const std::vector& items) { + delete _root; + _items = items; + _root = buildFromPoints(0, items.size()); + } + + // Function that uses the tree to find the k nearest neighbors of target + void search(const T& target, int k, std::vector* results, std::vector* distances) + { + + // Use a priority queue to store intermediate results on + std::priority_queue heap; + + // Variable that tracks the distance to the farthest point in our results + _tau = DBL_MAX; + + // Perform the search + search(_root, target, k, heap); + + // Gather final results + results->clear(); distances->clear(); + while(!heap.empty()) { + results->push_back(_items[heap.top().index]); + distances->push_back(heap.top().dist); + heap.pop(); + } + + // Results are in reverse order + std::reverse(results->begin(), results->end()); + std::reverse(distances->begin(), distances->end()); + } + +private: + std::vector _items; + double _tau; + + // Single node of a VP tree (has a point and radius; left children are closer to point than the radius) + struct Node + { + int index; // index of point in node + double threshold; // radius(?) + Node* left; // points closer by than threshold + Node* right; // points farther away than threshold + + Node() : + index(0), threshold(0.), left(0), right(0) {} + + ~Node() { // destructor + delete left; + delete right; + } + }* _root; + + + // An item on the intermediate result queue + struct HeapItem { + HeapItem( int index, double dist) : + index(index), dist(dist) {} + int index; + double dist; + bool operator<(const HeapItem& o) const { + return dist < o.dist; + } + }; + + // Distance comparator for use in std::nth_element + struct DistanceComparator + { + const T& item; + DistanceComparator(const T& item) : item(item) {} + bool operator()(const T& a, const T& b) { + return distance(item, a) < distance(item, b); + } + }; + + // Function that (recursively) fills the tree + Node* buildFromPoints( int lower, int upper ) + { + if (upper == lower) { // indicates that we're done here! + return NULL; + } + + // Lower index is center of current node + Node* node = new Node(); + node->index = lower; + + if (upper - lower > 1) { // if we did not arrive at leaf yet + + // Choose an arbitrary point and move it to the start + int i = (int) ((double)rand() / RAND_MAX * (upper - lower - 1)) + lower; + std::swap(_items[lower], _items[i]); + + // Partition around the median distance + int median = (upper + lower) / 2; + std::nth_element(_items.begin() + lower + 1, + _items.begin() + median, + _items.begin() + upper, + DistanceComparator(_items[lower])); + + // Threshold of the new node will be the distance to the median + node->threshold = distance(_items[lower], _items[median]); + + // Recursively build tree + node->index = lower; + node->left = buildFromPoints(lower + 1, median); + node->right = buildFromPoints(median, upper); + } + + // Return result + return node; + } + + // Helper function that searches the tree + void search(Node* node, const T& target, int k, std::priority_queue& heap) + { + if(node == NULL) return; // indicates that we're done here + + // Compute distance between target and current node + double dist = distance(_items[node->index], target); + + // If current node within radius tau + if(dist < _tau) { + if(heap.size() == k) heap.pop(); // remove furthest node from result list (if we already have k results) + heap.push(HeapItem(node->index, dist)); // add current node to result list + if(heap.size() == k) _tau = heap.top().dist; // update value of tau (farthest point in result list) + } + + // Return if we arrived at a leaf + if(node->left == NULL && node->right == NULL) { + return; + } + + // If the target lies within the radius of ball + if(dist < node->threshold) { + if(dist - _tau <= node->threshold) { // if there can still be neighbors inside the ball, recursively search left child first + search(node->left, target, k, heap); + } + + if(dist + _tau >= node->threshold) { // if there can still be neighbors outside the ball, recursively search right child + search(node->right, target, k, heap); + } + + // If the target lies outsize the radius of the ball + } else { + if(dist + _tau >= node->threshold) { // if there can still be neighbors outside the ball, recursively search right child first + search(node->right, target, k, heap); + } + + if (dist - _tau <= node->threshold) { // if there can still be neighbors inside the ball, recursively search left child + search(node->left, target, k, heap); + } + } + } +}; + +#endif diff --git a/src/ofxTSNE.cpp b/src/ofxTSNE.cpp new file mode 100644 index 0000000..d307fa5 --- /dev/null +++ b/src/ofxTSNE.cpp @@ -0,0 +1,69 @@ +#include "ofxTSNE.h" + + +vector > ofxTSNE::run(vector > & data, int dims, double perplexity, double theta, bool normalize) { + int N, D; + double *X, *Y; + + N = data.size(); + D = data[0].size(); + + if (N - 1 < 3 * perplexity) { + ofLog(OF_LOG_WARNING, "Perplexity too large for number of data points, setting to max"); + perplexity = (float) (N-1) / 3.0 - 1.0; + } + + X = (double*) malloc(D * N * sizeof(double)); + Y = (double*) malloc(dims * N * sizeof(double)); + + int idx = 0; + for (int i=0; i min_, max_; + min_.resize(dims); + max_.resize(dims); + for (int i=0; i::max(); + max_[i] = numeric_limits::min(); + } + + // unpack Y into tsnePoints + tsnePoints.clear(); + int idxY = 0; + for (int i=0; i tsnePoint; + tsnePoint.resize(dims); + for (int j=0; j max_[j]) max_[j] = tsnePoint[j]; + } + idxY++; + } + tsnePoints.push_back(tsnePoint); + } + + // normalize if requested + if (normalize) { + for (int i=0; i > run(vector > & data, int dims=2, double perplexity=30, double theta=0.5, bool normalize=true); +private: + TSNE tsne; + vector > tsnePoints; +};