diff --git a/.gitignore b/.gitignore index 5c20b7643b981502540fe3064e1bd6021359c6e4..321544b79e5399b5cd231d076b48e588f2f64631 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,6 @@ docs/source/monomulti/.ipynb_checkpoints/** results/* data/* Data/* -multiview_platform/MonoMultiviewClassifiers/Results/* multiview_platform/tests/temp_tests/** multiview-machine-learning-omis.iml multiview_platform.egg-inf* @@ -25,4 +24,9 @@ multiview_platform/examples/results/example_5/* multiview_platform/html_cov/ multiview_platform/.coverage* .coverage* -htmlcov/ \ No newline at end of file +htmlcov/ +.gitignore +.cache/ +.pytest_cache/ +multiview_platform/.xml +summit.egg-info/ \ No newline at end of file diff --git a/ipynb/.ipynb_checkpoints/FeatureExtraction-All-checkpoint.ipynb b/ipynb/.ipynb_checkpoints/FeatureExtraction-All-checkpoint.ipynb deleted file mode 100644 index 34e8e06ce8f26758b19fe9dd7232c4659903e4fe..0000000000000000000000000000000000000000 --- a/ipynb/.ipynb_checkpoints/FeatureExtraction-All-checkpoint.ipynb +++ /dev/null @@ -1,707 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Code to Extract all Features from Database" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Author: Nikolas Hülsmann\n", - "#### Date: 2015-12-06" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Functions for Extract Data\n", - "\n", - "### Function to iterate through given directory and return images paths and classLabels" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "def imgCrawl(path, sClassLabels): #path to 'highest' folder\n", - " rootdir = path\n", - " df = pd.DataFrame()\n", - " \n", - " for subdir, dirs, files in os.walk(rootdir): # loop through subdirectories\n", - " for file in files:\n", - " pathOfFile = os.path.join(subdir, file) #path of file\n", - " head, classLabel = os.path.split(os.path.split(pathOfFile)[0]) # get directoryname of file as classLabel\n", - " \n", - " # assign integer label for dataframe\n", - " classLabel = sClassLabels[sClassLabels == classLabel].index[0]\n", - " df = df.append({'classLabel': classLabel, 'pathOfFile': pathOfFile}, ignore_index=True) \n", - " \n", - " return df" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Function to determine Class-Labels with Integer representation" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# function to determine Class-labels and return Series\n", - "def getClassLabels(path):\n", - " data = os.listdir(path) # listdir returns all subdirectories\n", - " index = range(0,len(data))\n", - " \n", - " return pd.Series(data,index)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Function to calculate the RGBColorHistogram for given Images " - ] - }, - { - "cell_type": "code", - "execution_count": 44, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "#### Calculate RGBColorHistograms for all images\n", - "\n", - "### Points to improve: \n", - "\n", - "# dfImages: Dataframe with paths to all images - use function imgCrawl\n", - "# numberOfBins_: Number of bins Histogram\n", - "def calcRGBColorHisto(dfImages_, numberOfBins_):\n", - " # Initialize function\n", - "\n", - " npImages = dfImages_.values\n", - " numberOfBins = numberOfBins_\n", - " npColorHist = np.zeros((len(npImages), numberOfBins*3), \"float32\")\n", - " i=0\n", - " \n", - " ## algo\n", - " for images in npImages:\n", - " image = cv2.imread(images[1])\n", - " \n", - " # Image Size for Normalization\n", - " #height, width, channels = image.shape\n", - " #img_size = height * width\n", - " \n", - " # Split into color chanels rgb\n", - " chans = cv2.split(image)\n", - " colors = (\"b\", \"g\", \"r\")\n", - " \n", - " histogram = []\n", - "\n", - " ########### Feature Color Histogram (cf. http://docs.opencv.org/2.4/doc/tutorials/imgproc/histograms/histogram_calculation/histogram_calculation.html) # loop over the image channels\n", - " for (chan, color) in zip(chans, colors): \n", - " \n", - " # Calculate Color Histogram - 16 bins cf. paper (test with 64 has shown that die result is similair in score)\n", - " # Seperates the intesity for each color from 0 to 256, and creates 16 bins of same size: 0-15, 16-31, .. , 241-256\n", - " hist = cv2.calcHist([chan], [0], None, [numberOfBins], [0, 256])\n", - "\n", - " # to get raw values\n", - " hist = hist[:,0]\n", - " \n", - " # Normalize to a Distrubution from 0 to 1 throug calculating for each color channel (red/blue/green): \n", - " # (number of pixels in bin)/(sum of pixels in histogram)\n", - " #hist[:] = [x / img_size for x in hist]\n", - " sumHist = sum(hist)\n", - " if(sumHist>0):\n", - " hist[:] = [x / sumHist for x in hist]\n", - " else:\n", - " print colored(\"WARNING NORMELIZATION: sumHIST is zero (0)\", 'red')\n", - " print colored(\"image: \" + images[1] + \"\\n\", 'red')\n", - " \n", - "\n", - " # Normalize with MinMax from 0 to 1 -> feature scaling\n", - " #cv2.normalize(hist, hist, 0, 1, cv2.NORM_MINMAX)\n", - " \n", - " histogram.extend(hist)\n", - "\n", - " # append features_colHist to npColorHist\n", - " npColorHist[i] = histogram\n", - " i = i+1\n", - " \n", - " \n", - " return npColorHist" - ] - }, - { - "cell_type": "code", - "execution_count": 43, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "#### Calculate HSVColorHistograms for all images\n", - "\n", - "### Points to improve: \n", - "\n", - "# dfImages: Dataframe with paths to all images - use function imgCrawl\n", - "# histSize: Number of bins in Histogram (differenz for each channel H,S,V)\n", - "def calcHSVColorHisto(dfImages_, histSize_):\n", - " # Initialize function\n", - " npImages = dfImages_.values\n", - " histSize = histSize_\n", - " npColorHist = np.zeros((len(npImages), int(histSize[0]+histSize[1]+histSize[2])), \"float32\")\n", - " i=0\n", - "\n", - " h_ranges = [ 0, 180 ]\n", - " s_ranges = [ 0, 256 ]\n", - " v_ranges = [ 0, 256 ]\n", - "\n", - " ranges = []\n", - "\n", - " ranges.append(h_ranges)\n", - " ranges.append(s_ranges)\n", - " ranges.append(v_ranges)\n", - "\n", - " channels = [ 0, 1, 2]\n", - " \n", - " histogram = []\n", - " \n", - " ## algo\n", - " for images in npImages:\n", - " image = cv2.imread(images[1])\n", - " hsv = cv2.cvtColor(img,cv2.COLOR_BGR2HSV)\n", - " \n", - " \n", - " # Split into color chanels rgb\n", - " chans = cv2.split(image)\n", - " colors = (\"H\", \"S\", \"V\")\n", - " \n", - " histogram = []\n", - " \n", - " ########### Feature Color Histogram (cf. http://docs.opencv.org/2.4/doc/tutorials/imgproc/histograms/histogram_calculation/histogram_calculation.html) # loop over the image channels\n", - " for (chan, color, binsize, range_chan) in zip(chans, colors, histSize, ranges): \n", - " hist = cv2.calcHist([chan], [0], None, [binsize], range_chan )\n", - " \n", - " # to get raw values\n", - " hist = hist[:,0]\n", - " \n", - " \n", - " # Normalize to a Distrubution from 0 to 1 throug calculating for each color channel (H/S/V): \n", - " # (number of pixels in bin)/(sum of pixels in histogram)\n", - " #hist[:] = [x / img_size for x in hist]\n", - " sumHist = sum(hist)\n", - " if(sumHist>0):\n", - " hist[:] = [x / sumHist for x in hist]\n", - " else:\n", - " print colored(\"WARNING NORMELIZATION: sumHIST is zero (0)\", 'red')\n", - " print colored(\"image: \" + images[1] + \"\\n\", 'red')\n", - " \n", - " histogram.extend(hist)\n", - " \n", - " \n", - " # append features_colHist to npColorHist\n", - " npColorHist[i] = histogram\n", - " i = i+1\n", - " \n", - " \n", - " return npColorHist\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Function to calculate Surf Histogram" - ] - }, - { - "cell_type": "code", - "execution_count": 42, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "################# FEATURE SURF (cf. http://docs.opencv.org/3.0-beta/doc/py_tutorials/py_feature2d/py_surf_intro/py_surf_intro.html#surf)\n", - "# API cf. http://docs.opencv.org/2.4/modules/nonfree/doc/feature_detection.html\n", - "\n", - "#### Calculate Histogramm of SURF Descripteurs with Bag Of Words appraoch for all images\n", - "\n", - "### Points to improve: \n", - "# - use spatial histogram: http://www.di.ens.fr/willow/events/cvml2011/materials/practical-classification/\n", - "# - change function: parameter how many K clustes/feature length (in regard of overfitting)\n", - "\n", - "\n", - "# path to higehst folder\n", - "# dfImages: Dataframe with paths to all images - use function imgCrawl\n", - "# k: number of K-Cluster -> length of feature vector\n", - "def calcSurfHisto(dfImages_, k_):\n", - " \n", - " # Initialize function\n", - " npImages = dfImages_.values\n", - " k = k_\n", - " \n", - " # List where all the descriptors are stored\n", - " des_list = []\n", - " \n", - " #### Feature Detection and Description (Surf): \n", - " # Detect (localize) for each image the keypoints (Points of Interest in an image - Surf uses therefore like SIFT corners)\n", - " # Pro: SIFT/SURF are scale and rotation invariant!\n", - " for images in npImages:\n", - " # Read image\n", - " image = cv2.imread(images[1], cv2.CV_LOAD_IMAGE_COLOR)\n", - " #cv2.cvtColor(image, gray, cv.CV_BGR2GRAY);\n", - " \n", - " # Method to detect keypoints (kp) and calculate the descripteurs (des) with one function call\n", - " # Each image has different amount of kp, but each kp has a describteur of fixed length (128)\n", - " kp, des = sift.detectAndCompute(image,None)\n", - " des_list.append(des)\n", - " \n", - " # Stack all the descriptors vertically in a numpy array\n", - " descriptors = des_list[0]\n", - " for descriptor in des_list[0:]:\n", - " descriptors = np.vstack((descriptors, descriptor)) \n", - " \n", - " #### Bag of Words Approach\n", - " ### 1. Step: using K-means cluster to create dictionary/vocabulary/codebook:\n", - " # Encoding is the quantization of the image kp/des that constitute the image to be classified. \n", - " # Basic encoding schemes work by first running K-means on the set of all des that you collect \n", - " # across multiple images.\n", - " # This builds what is known a dictionary/vocabulary/codebook represented by the centroids obtained from the clustering.\n", - " \n", - " # Perform k-means clustering -> creates the words from all describteurs -> this is the (dic) dictionary/vocabulary/codebook\n", - " # k: amount of different clusters to build! Will result in a feature length k\n", - " dic, variance = kmeans(descriptors, k, 1) \n", - " \n", - " ### 2. Step: encoding/coding/vector quantization(vq) to assign each descripteur the closest \"visual word\" from dictionary:\n", - " # At the end of this process, you end up with K representative \"visual words\" (the centroid of each cluster after \n", - " # K means ends) of your image descripteurs. These \"visual words\" represent what is usually understood as your \n", - " # visual dictionary. Once you have these visual words, encoding is the process of assigning \n", - " # each descripteur within your image the \"visual word\" (nearest neighbor) in the dictionary.\n", - " \n", - " npSurfHist = np.zeros((len(npImages), k), \"float32\")\n", - " for i in xrange(len(npImages)):\n", - " # vq: (Encoding) Assign words from the dictionary to each descripteur\n", - " words, distance = vq(des_list[i],dic)\n", - " \n", - " ### 3. Step: Pooling - calculate a histogram for each image\n", - " # Pooling refers to the process of representing an image as a \"bag of words\". \n", - " # The word bag here is meant to convey that once you have encoded each descripteur with a word (a number between 1 and K), \n", - " # you build a new representation (a bag) that discards the spatial relationship between the words that \n", - " # constitute your image.\n", - "\n", - " # This representation is often a histogram or a collection of spatially adjacent histograms of the desribteurs \n", - " # (i.e. histograms of values 1 to K) that together form your image. \"Pooling\" is thus the process of \n", - " # building a histogram of words (i.e. pooling ~ \"sampling\" words from the image to build a probability \n", - " # mass function of words)\n", - "\n", - " # To clarify, the purpose of pooling is two fold:\n", - " # By building a feature vector that is a histogram of words (as opposed to putting the full \"sentence of words\" \n", - " # in the feature vector), your descriptor will be invariant to changes in \"the ordering of words\". \n", - " # In computer vision this translates into invariance with respect to rotations and distortions of the image \n", - " # and object, which is a desirable thing to have.\n", - "\n", - " # If the dictionary is small compared to the length of the sentence, a histogram of words has less dimensions \n", - " # than the original vector. Less dimensions makes learning (training) much easier.\n", - " \n", - " \n", - " # Count the accuarance of each word (w) in image (i) to build histogram\n", - " for w in words:\n", - " npSurfHist[i][w] += 1\n", - " \n", - " #### 4. Step: Normalization of features vector (Can be changed to distribution like ColorHisto)\n", - " # Frequency divided by amount of words (k)\n", - " sumHist = sum(npSurfHist[i])\n", - " if(sumHist>0):\n", - " for x in range(0,k):\n", - " #npSurfHist[i][x] = npSurfHist[i][x]/k\n", - " npSurfHist[i][x] = npSurfHist[i][x]/sumHist #sumHist can be zero...change system\n", - " else: \n", - " print colored(\"WARNING NORMELIZATION: sumHIST is zero (0)\", 'red')\n", - " print colored(\"image: \" + images[1] + \"\\n\", 'red')\n", - " \n", - " \n", - " #stdSlr = StandardScaler().fit(npSurfHist)\n", - " #npSurfHist = stdSlr.transform(npSurfHist)\n", - " \n", - " return npSurfHist" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### SIFT Experimental - use SURF " - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "collapsed": false - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "(100L, 128L)\n" - ] - } - ], - "source": [ - "########### Feature SIFT (Scale-invariant feature transform cf. http://docs.opencv.org/master/da/df5/tutorial_py_sift_intro.html#gsc.tab=0)\n", - "# Api cf. http://docs.opencv.org/2.4/modules/nonfree/doc/feature_detection.html\n", - "import cv2\n", - "import numpy as np\n", - "\n", - "img = cv2.imread('D:/Caltech//airplanes//image_0306.jpg')\n", - "gray= cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)\n", - "\n", - "sift = cv2.SIFT(nfeatures=100)\n", - "#sift = cv2.xfeatures2d.SIFT_create()\n", - "\n", - "# Detector which detects the Keypoints in the Image\n", - "#kp = sift.detect(gray,None)\n", - "\n", - "# Just a visualization of the Keypoints in the Image\n", - "#img=cv2.drawKeypoints(gray,kp)\n", - "#cv2.imwrite('D:\\Sift-test\\sift_keypoints.jpg',img)\n", - "\n", - "# Another visualization with FLAG: draw a circle with size of keypoint and it will even show its orientation\n", - "#img=cv2.drawKeypoints(gray,kp,flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)\n", - "#cv2.imwrite('D:\\Sift-test\\sift_keypoints.jpg',img)\n", - "\n", - "# Method to compute the descripteurs after one has already detected the keypoints\n", - "#kp,des = sift.compute(gray,kp)\n", - "\n", - "#sift = cv2.xfeatures2d.SIFT_create()\n", - "#sift = cv2.SIFT()\n", - "\n", - "# Method to detect keypoints (kp) and calculate the descripteurs (des) with one function call\n", - "kp, des = sift.detectAndCompute(gray,None)\n", - "\n", - "print des.shape\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Functions to export calculated Data to csv " - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "#### Export Features to csv\n", - "def exportToCSV(pandasSorDF, filename):\n", - " #filename = datetime.datetime.now().strftime(\"%Y_%m_%d\") + \"-Feature\"\n", - " path = os.getcwdu() + \"\\\\\" + filename\n", - " \n", - " if os.path.isfile(path + \".csv\"):\n", - " for i in range(1,20):\n", - " testFileName = filename + \"-\" + str(i) + \".csv\"\n", - " if os.path.isfile(os.getcwdu() + \"\\\\\" + testFileName)!=True:\n", - " pandasSorDF.to_csv(testFileName)\n", - " break\n", - "\n", - " else:\n", - " pandasSorDF.to_csv(filename + \".csv\")\n" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def exportNumpyToCSV(numpyArray, filename):\n", - " #filename = datetime.datetime.now().strftime(\"%Y_%m_%d\") + \"-Feature\"\n", - " path = os.getcwdu() + \"\\\\\" + filename\n", - " \n", - " if os.path.isfile(path + \".csv\"):\n", - " for i in range(1,20):\n", - " testFileName = filename + \"-\" + str(i) + \".csv\"\n", - " if os.path.isfile(os.getcwdu() + \"\\\\\" + testFileName)!=True:\n", - " np.savetxt(testFileName, numpyArray, delimiter=\",\")\n", - " break\n", - "\n", - " else:\n", - " np.savetxt(filename + \".csv\", numpyArray, delimiter=\",\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Main Programm\n" - ] - }, - { - "cell_type": "code", - "execution_count": 40, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "# Imports\n", - "import os # for iteration throug directories\n", - "import pandas as pd # for Series and DataFrames\n", - "import cv2 # for OpenCV \n", - "import cv # for OpenCV\n", - "import datetime # for TimeStamp in CSVFile\n", - "from scipy.cluster.vq import * # for Clustering http://docs.scipy.org/doc/scipy/reference/cluster.vq.html\n", - "import numpy as np # for arrays\n", - "import time # for time calculations\n", - "from termcolor import colored #to color Warnings - PACKAGE MIGHT NOT BE AVAILABLE" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Extract images path from DataBase " - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": { - "collapsed": false - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "(9144L,)\n", - "Time to extract all images: 26.3950002193\n" - ] - } - ], - "source": [ - "start = time.time()\n", - "\n", - "# Determine the Database to extract features\n", - "path ='D:\\Caltech'\n", - "\n", - "# get dictionary to link classLabels Text to Integers\n", - "sClassLabels = getClassLabels(path)\n", - "\n", - "# Get all path from all images inclusive classLabel as Integer\n", - "dfImages = imgCrawl(path, sClassLabels)\n", - "print dfImages.classLabel.shape\n", - "\n", - "end = time.time()\n", - "print \"Time to extract all images: \" + str(end - start)" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "# CSV Output\n", - "fileNameClassLabels = datetime.datetime.now().strftime(\"%Y_%m_%d\") + \"-Caltech-ClassLabels\"\n", - "exportNumpyToCSV(dfImages.classLabel, fileNameClassLabels)\n", - "\n", - "fileNameClassLabels = datetime.datetime.now().strftime(\"%Y_%m_%d\") + \"-Caltech-ClassLabels-Description\"\n", - "exportToCSV(sClassLabels, fileNameClassLabels)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### RGB Color Histogram " - ] - }, - { - "cell_type": "code", - "execution_count": 47, - "metadata": { - "collapsed": false - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "(9144L, 48L)\n", - "Time to calculate RGBColorHistogram: 19.3360002041\n" - ] - } - ], - "source": [ - "start = time.time()\n", - "\n", - "# Calculate RGB Color Histogram wit 16 bins for each color -> feature length = 3 x 16 = 48\n", - "npRGBColorHistogram = calcRGBColorHisto(dfImages, 16)\n", - "print npRGBColorHistogram.shape\n", - "\n", - "end = time.time()\n", - "print \"Time to calculate RGBColorHistogram: \" + str(end - start)" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# CSV Output\n", - "fileNameColorHis = datetime.datetime.now().strftime(\"%Y_%m_%d\") + \"-Caltech-Feature-RGBColorHistogram\"\n", - "exportNumpyToCSV(npRGBColorHistogram, fileNameColorHis)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### HSV Color Histogram " - ] - }, - { - "cell_type": "code", - "execution_count": 45, - "metadata": { - "collapsed": false - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\u001b[31mWARNING NORMELIZATION: sumHIST is zero (0)\u001b[0m\n", - "\u001b[31mimage: D:\\Caltech\\BACKGROUND_Google\\image_0031.jpg\n", - "\u001b[0m\n", - "\u001b[31mWARNING NORMELIZATION: sumHIST is zero (0)\u001b[0m\n", - "\u001b[31mimage: D:\\Caltech\\menorah\\image_0022.jpg\n", - "\u001b[0m\n", - "(9144L, 14L)\n", - "Time to calculate HSVColorHistogram: 22.4220001698\n" - ] - } - ], - "source": [ - "start = time.time()\n", - "\n", - "# Calculate HSV Color Histogramm -> feature length = 8+3+3 = 14\n", - "\n", - "h_bins = 8 \n", - "s_bins = 3\n", - "v_bins = 3\n", - "histSize = [ h_bins, s_bins, v_bins ]\n", - "\n", - "npHSVColorHistogram = calcHSVColorHisto(dfImages, histSize)\n", - "print npHSVColorHistogram.shape\n", - "\n", - "end = time.time()\n", - "print \"Time to calculate HSVColorHistogram: \" + str(end - start)" - ] - }, - { - "cell_type": "code", - "execution_count": 46, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# CSV Output\n", - "fileNameColorHis = datetime.datetime.now().strftime(\"%Y_%m_%d\") + \"-Caltech-Feature-HSVColorHistogram\"\n", - "exportNumpyToCSV(npHSVColorHistogram, fileNameColorHis)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### SURF Histogram " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "start = time.time()\n", - "\n", - "# Calculate Surf Histogramm with K=100 Cluster\n", - "npSurfHistogram = calcSurfHisto(dfImages, 100)\n", - "print npSurfHistogram.shape\n", - "\n", - "end = time.time()\n", - "print \"Time to calculate SurfHistogram: \" + str(end - start)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# CSV Output\n", - "fileNameSurfHis = datetime.datetime.now().strftime(\"%Y_%m_%d\") + \"-Caltech-Feature-SurfHistogram\"\n", - "exportNumpyToCSV(npSurfHistogram, fileNameSurfHis)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 2", - "language": "python", - "name": "python2" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 2 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython2", - "version": "2.7.11" - } - }, - "nbformat": 4, - "nbformat_minor": 0 -} diff --git a/ipynb/.ipynb_checkpoints/HOG Extraction-checkpoint.ipynb b/ipynb/.ipynb_checkpoints/HOG Extraction-checkpoint.ipynb deleted file mode 100644 index baafb339c6d77094303a9b186d45f1009c3501f0..0000000000000000000000000000000000000000 --- a/ipynb/.ipynb_checkpoints/HOG Extraction-checkpoint.ipynb +++ /dev/null @@ -1,354 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "<h1>Imports </h1>" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "import os as os # for iteration throug directories\n", - "import pandas as pd # for Series and DataFrames\n", - "import cv2 # for OpenCV \n", - "import numpy as np # for arrays\n", - "import time # for time calculations\n", - "from feature_extraction_try import imgCrawl, getClassLabels #for fetching images\n", - "from skimage.feature import hog #calculates HOGs \n", - "from sklearn.cluster import MiniBatchKMeans #for clustering" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "<h1>HOG computation :</h1>\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In this file, we aim to compute a HOG descriptor thas has the same dimension for each image. So we used a bag of word approach : \n", - "<ul>\n", - " <li>Resizing images to a 0mod5 height and width</li>\n", - " <li>Sequencing each image in 5*5 cells</li>\n", - " <li>Computing local histograms on each cell</li>\n", - " <li>Clustering the local histograms</li>\n", - " <li>Binning the local histograms to create our feature</li>\n", - "</ul>" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "<h2>Resizing images : </h2>" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We decided to resize images by duplicating the missing pixels on each side (width and height) because it seem to be the less disturbing way for gradient computation. \n", - "Another possible way is to crop each image, but it may delete useful information. \n", - "Returns the resized image as a np.array " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def reSize(image, CELL_DIMENSION):\n", - " height, width, channels = image.shape\n", - " \n", - " if height%CELL_DIMENSION==0 and width%CELL_DIMENSION==0:\n", - " resizedImage = image\n", - " \n", - " elif width%CELL_DIMENSION==0:\n", - " missingPixels = CELL_DIMENSION-height%CELL_DIMENSION\n", - " resizedImage = cv2.copyMakeBorder(image,0,missingPixels,0,\\\n", - " 0,cv2.BORDER_REPLICATE)\n", - " \n", - " elif height%CELL_DIMENSION==0:\n", - " missingPixels = CELL_DIMENSION-width%CELL_DIMENSION\n", - " resizedImage = cv2.copyMakeBorder(image,0,0,0,missingPixels,\\\n", - " cv2.BORDER_REPLICATE)\n", - " \n", - " else:\n", - " missingWidthPixels = CELL_DIMENSION-width%CELL_DIMENSION\n", - " missingHeightPixels = CELL_DIMENSION-height%CELL_DIMENSION\n", - " resizedImage = cv2.copyMakeBorder(image,0,missingHeightPixels,0,\\\n", - " missingWidthPixels,cv2.BORDER_REPLICATE)\n", - " return resizedImage" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "<h2>Sequencing images : </h2>" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here, we just tranfrom our corpus in $(5*5)$ cells to be able to execute the bag of words approach. \n", - "\n", - "Returns a (nbImages$*$imageHeight mod 5$*$imageWidth mod 5$*$nbChannels) np.array" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def imageSequencing(npImages, CELL_DIMENSION):\n", - " cells=[]\n", - " \n", - " for k in range(len(npImages)):\n", - " image = cv2.imread(npImages[k][1])\n", - " resizedImage = reSize(image, CELL_DIMENSION)\n", - " height, width, channels = resizedImage.shape\n", - " cells.append(\\\n", - " np.array([\\\n", - " resizedImage[\\\n", - " j*CELL_DIMENSION:j*CELL_DIMENSION+CELL_DIMENSION,\\\n", - " i*CELL_DIMENSION:i*CELL_DIMENSION+CELL_DIMENSION] \\\n", - " for i in range(width/CELL_DIMENSION) \\\n", - " for j in range(height/CELL_DIMENSION)\\\n", - " ])\\\n", - " )\n", - " return np.array(cells) " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "<h2>Compute HOG descriptor on each cell : </h2>" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here we convert our images in grayscale toi be able to apply our localHistogram function (doc : http://scikit-image.org/docs/dev/auto_examples/plot_hog.html)\n", - "\n", - "Then we compute local HOGs on our cells with NB_ORIENTATIONS orientations" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def computeLocalHistograms(cells, NB_ORIENTATIONS, CELL_DIMENSION):\n", - " localHistograms = np.array([\\\n", - " np.array([\\\n", - " hog(cv2.cvtColor( cell, \\\n", - " cv2.COLOR_BGR2GRAY), \\\n", - " orientations=NB_ORIENTATIONS, \\\n", - " pixels_per_cell=(CELL_DIMENSION,\\\n", - " CELL_DIMENSION),\\\n", - " cells_per_block=(1,1)) \\\n", - " for cell in image]) \\\n", - " for image in cells])\n", - " return localHistograms" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "<h2>Clustering local HOGs : </h2>" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here we search for NB_CLUSTERS clusters in all our localHistograms to be able to bin later our localHistograms.\n", - "\n", - "doc for MiniBatchKMeans : http://scikit-learn.org/stable/modules/generated/sklearn.cluster.MiniBatchKMeans.html" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def clusterGradients(localHistograms, NB_CLUSTERS, MAXITER):\n", - " sizes = np.array([len(localHistogram) for localHistogram in localHistograms])\n", - " nbImages = len(localHistograms)\n", - " flattenedHogs = np.array([cell for image in localHistograms for cell in image])\n", - " miniBatchKMeans = MiniBatchKMeans(n_clusters=NB_CLUSTERS, max_iter=MAXITER, \\\n", - " compute_labels=True)\n", - " localHistogramLabels = miniBatchKMeans.fit_predict(flattenedHogs)\n", - " return localHistogramLabels, sizes" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "<h2>Binning local HOGs : </h2>" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Then we just need to bin our local HOGs to avec a constant-sized feature based on HOG to describe our images" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def makeHistograms(labels, NB_CLUSTERS, sizes):\n", - " indiceInLabels = 0\n", - " hogs = []\n", - " for image in sizes:\n", - " histogram = np.zeros(NB_CLUSTERS)\n", - " for i in range(image):\n", - " histogram[labels[indiceInLabels+i]] += 1\n", - " hogs.append(histogram)\n", - " indiceInLabels+=i \n", - " return np.array(hogs)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "<h2>Global function</h2>" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def extractHOGFeature(npImages, CELL_DIMENSION, NB_ORIENTATIONS, \\\n", - " NB_CLUSTERS, MAXITER):\n", - " cells = imageSequencing(npImages, CELL_DIMENSION)\n", - " localHistograms = computeLocalHistograms(cells)\n", - " localHistogramLabels, sizes = clusterGradients(localHistograms, \\\n", - " NB_CLUSTERS, MAXITER)\n", - " hogs = makeHistograms(localHistogramLabels, NB_CLUSTERS, sizes)\n", - " return hogs" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "<h1>Test zone</h1>" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "if __name__ == '__main__':\n", - "\n", - "\n", - " start = time.time()\n", - " path ='/home/doob/Dropbox/Marseille/OMIS-Projet/03-jeux-de-donnees/101_ObjectCategories'\n", - " testNpImages = [ [1,'testImage.jpg'] ]\n", - " CELL_DIMENSION = 5\n", - " NB_ORIENTATIONS = 8\n", - " NB_CLUSTERS = 12\n", - " MAXITER = 100\n", - "\n", - " print \"Fetching Images in \" + path\n", - " # get dictionary to link classLabels Text to Integers\n", - " sClassLabels = getClassLabels(path)\n", - " # Get all path from all images inclusive classLabel as Integer\n", - " dfImages = imgCrawl(path, sClassLabels)\n", - " npImages = dfImages.values\n", - " extractedTime = time.time()\n", - " print \"Extracted images in \" + str(extractedTime-start) +'sec'\n", - " print \"Sequencing Images ...\"\n", - " blocks = imageSequencing(testNpImages, 5)\n", - " sequencedTime = time.time()\n", - " print \"Sequenced images in \" + str(sequencedTime-extractedTime) +'sec'\n", - " print \"Computing gradient on each block ...\"\n", - " gradients = computeLocalHistograms(blocks, NB_ORIENTATIONS, CELL_DIMENSION)\n", - " hogedTime = time.time()\n", - " print \"Computed gradients in \" + str(hogedTime - sequencedTime) + 'sec'\n", - " print \"Clustering gradients ...\"\n", - " gradientLabels, sizes = clusterGradients(gradients, NB_CLUSTERS, MAXITER)\n", - " clusteredItme = time.time()\n", - " print \"Clustered gradients in \" + str(hogedTime - sequencedTime) + 'sec'\n", - " print \"Computing histograms ...\"\n", - " histograms = makeHistograms(gradientLabels, NB_CLUSTERS, sizes)\n", - " end = time.time()\n", - " print \"Computed histograms in \" + str(int(end - hogedTime)) + 'sec'\n", - " print \"Histogram shape : \" +str(histograms.shape)\n", - " print \"Total time : \" + str(end-start) + 'sec'\n", - " #hogs = extractHOGFeature(testNpImages, CELL_DIMENSION, \\\n", - " # NB_ORIENTATIONS, NB_CLUSTERS, MAXITER)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 2", - "language": "python", - "name": "python2" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 2 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython2", - "version": "2.7.11" - } - }, - "nbformat": 4, - "nbformat_minor": 0 -} diff --git a/ipynb/Adding a multiview classifier.ipynb b/ipynb/Adding a multiview classifier.ipynb deleted file mode 100644 index 47cc365af44eb49bd06fd5d2f1b36e19bfa4411a..0000000000000000000000000000000000000000 --- a/ipynb/Adding a multiview classifier.ipynb +++ /dev/null @@ -1,551 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# How to add a multiview classifier to the platform" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## File addition " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "* In the `Code/MonoMultiViewClassifiers/MultiviewClassifiers` package, add a new package named after your multiview classifier (let's call it NMC for New Multiview Classifier).\n", - "\n", - "* In this package (`Code/MonoMultiViewClassifiers/MultiviewClassifiers/NMC`), add a file called `NMCModule.py` and another one called `analyzeResults.py`. These will be the two files used by the platform to communicate with your implementation.\n", - "\n", - "* You can now add either a package named after your classifier `NMCPackage` and paste your files in it or just add a file with the same name if it is enough." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## `NMCModule.py`" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here we will list all the necessary functions of the python module to allow the platform to use NMC" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### The functions" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### `getArgs`" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This function is used to multiple arguments dictionaries from one benchmark entry. It must return the `argumentsList` to which it must have add at least a dictionary containing all the necessary information to run NMC. You must add all general fields about the type of classifier and a field called `NMCKWARGS` (`<classifier_name>KWARGS`) conataining another dictionary with classifier-specific agruments (we assume here that NMC has two hyper-parameters : a set of weights and an integer) " - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "arguments = {\"CL_type\":\"NMC\", \n", - " \"views\":[\"all\", \"the\", \"views\", \"names\"],\n", - " \"NB_VIEW\":len([\"all\", \"the\", \"views\", \"names\"]), \n", - " \"viewsIndices\":[\"the indices\", \"of the\", \"views in\", \"the hdf5 file\"], \n", - " \"NB_CLASS\": \"the number of labels of the dataset\", \n", - " \"LABLELS_NAMES\": [\"the names of\", \"the labels used\"], \n", - " \"NMCKWARGS\":{\"weights\":[], \n", - " \"integer\":42,\n", - " \"nbViews\":5}\n", - " }" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To fill these fields, you can use the default values given in argument of the function : " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def getArgs(args, benchmark, views, viewsIndices, randomState, directory, resultsMonoview, classificationIndices):\n", - " argumentsList = []\n", - " nbViews = len(views)\n", - " arguments = {\"CL_type\": \"NMC\",\n", - " \"views\": views,\n", - " \"NB_VIEW\": len(views),\n", - " \"viewsIndices\": viewsIndices,\n", - " \"NB_CLASS\": len(args.CL_classes),\n", - " \"LABELS_NAMES\": args.CL_classes,\n", - " \"NMCKWARGS\": {\"weights\":[],\n", - " \"integer\":42,\n", - " \"nbViews\":5}}\n", - " argumentsList.append(arguments)\n", - " return argumentsList" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This function is also used to add the user-defined configuration for the classifier, but we will discuss it later" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### `genName`" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This function is used to generate a short string describing the classifier using its configuration." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def genName(config):\n", - " return \"NMF\"" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Some classifiers, like some late fusion classifiers will have more complicated `genName` functions that will need to summurize which monoview classifiers they use in a short string using the `config` argument that is exactly the dictionay called `\"NMCKWARGS\"` in the `getArgs` function" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### `getBenchmark`" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This function is used to generate the `benchmark` argument of `getArgs`. It stores all the different configurations that will have to be tested (does not inculde hyper-parameters sets). For example for the Mumbo classifier, il will store the list of possible algorithms to use as weak leaners. " - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def getBenchmark(benchmark, args=None):\n", - " benchmark[\"Multiview\"][\"NMC\"] = [\"Some NMC cnfigurations\"]\n", - " return benchmark" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The `benchmark` argument is pre-generated with an entry for all the multiview classifiers so you just need to fill it with the different configurations" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### `genParamsSets`" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This function is used to generate random hyper-parameters sets to allow a randomized search to estimate the best one. It works in pair with the `setParams` method implemented in the classifier's class so you need to keep in mind the order of the hyper-paramters you used here.\n", - "\n", - "The `classificationKWARGS` argument is the `\"NMCKWARGS\"` entry seen earlier, and it is highly recommended to use the `randomState` object (which is described [here](https://docs.scipy.org/doc/numpy-1.13.0/reference/generated/numpy.random.RandomState.html)) to generate random numbers in order for the results to be reproductible\n", - "\n", - "Assuming our NMC classifier has 2 HP, one weight vector for each view and one integer that can be between 1 and 100, the `genParamsSets` function will look like :" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def genParamsSets(classificationKWARGS, randomState, nIter=1):\n", - " weightsVector = [randomState.random_sample(classificationKWARGS[\"nbViews\"]) for _ in range(nIter)]\n", - " nomralizedWeights = [weights/np.sum(weights) for weights in weightsVector]\n", - " intsVector = list(randomState.randint(1,100,nIter))\n", - " paramsSets = [[normalizedWeight, integer] for normalizedWeight, interger in zip(normalizedWeights, intsVector)]\n", - " return paramsSets" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### The `NMC` class" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "It has to be named after the classifier adding `Class` at the end of its name. " - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "class NMCClass:\n", - " pass" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### `init` method" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "There is nothing specific to define in the `__init__` method, you just need to initialize the attributes of your classifier. The `kwargs` argument is the `NMCKWARGS` dictionary seen earlier. In our example, NMC uses two hyper parameters : weights and an int." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def __init__(self, randomState, NB_CORES=1, **kwargs):\n", - " if kwargs[\"weights\"] == []:\n", - " self.weights = randomState.random_sample(classificationKWARGS[\"nbViews\"])\n", - " else:\n", - " self.weights = kwargs[\"weights\"]\n", - " self.weights /= np.sum(self.weights)\n", - " self.integer = kwargs[\"integer\"]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### `setParams` method" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This method is used to tune your classifier with a set of hyper parameters. The set is a list ordered as in the `genParamsSets` function seen earlier. The input of the `setParams` method is a list of parameters in the right order. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def setParams(self, paramsSet):\n", - " self.weights = paramsSet[0]\n", - " self.integer = paramsSet[1]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### `fit_hdf5` method" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This method is generaly the same as `sklearn`'s `fit` method but uses as an input an HDF5 dataset in order to lower the memory usage of the whole platform.\n", - "* The `DATASET` object is an HDF5 dataset file containing all the views and labels. \n", - "* The `usedIndices` object is a `numpy` 1d-array containing the indices of the examples want to learn from. \n", - "* The `viewsIndices` object is a `numpy` 1d-array containing the indices of the views we want to learn from. " - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def fit_hdf5(self, DATASET, usedIndices=None, viewsIndices=None):\n", - " # Call the fit function of your own module\n", - " pass" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### `predict_hdf5` method" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This method is used as an HDF5-compatible method similar to `sklearn`'s `predict` method. It has the same input than the `fit_hdf5` method but returns a 1d-array containing the labels of the asked examples (ordered as in `usedIndices`)." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "def predict_hdf5(self, DATASET, usedIndices=None, viewsIndices=None):\n", - " # Call the predict function of your own module\n", - " predictedLabels = None # Just to avoid any ipynb running error\n", - " return predictedLabels" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Once you've added everything to the `NMCModule.py` file you are close to be able to run your algorithm on the platform, you just need to fill the `analyzeResults.py` file." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## `analyzeResults.py`" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The `analyzeResults.py` file is a module used to get a specific result analysis for your classifier. You have, in order to run the platform, to add aunique function called `execute` that will run the analysis and return three different variables : \n", - "* `stringAnalysis` is a string that will be saved in a file to describe the classifier, its performance and may give some insights on the interpretation of it's way to classify. \n", - "* `imagesAnalysis` is a dictionary where you can store images (as values) to describe the classifier & co., the keys will be the images names. \n", - "* `metricsScores` is a dictionary where the values are lists containing train and test scores, and the keys are the metrics names. ( `metricsScores = {\"accuracy_score\":[0.99, 0.10]}` )\n", - "The `execute` function has as inputs : \n", - "* `classifier` is a classifier object from your classifiers class\n", - "* `trainLabels` are the labels predicted for the train set by the classifier\n", - "* `testLabels` are the labels predicted for the test set by the classifier\n", - "* `DATASET` is the HDF5 dataset object\n", - "* `classificationKWARGS` is the dictionary named `NMCKWARGS` earlier\n", - "* `classificationIndices` is a triplet containing the learning indices, the validation indices and the testIndices for multiclass classification\n", - "* `LABELS_DICTIONARY` is a dictionary containing a label as a key and it's name as a value\n", - "* `views` is the list of the views names used by the classifier\n", - "* `nbCores` is an `int` fixing the number of threads used by the platform \n", - "* `times` is a tuple containing the extraction time and the classification time\n", - "* `name` is the name ofthe database on which the plartform is running\n", - "* `KFolds` is an `sklearn` kfold object used for the cross-validation\n", - "* `hyperParamSearch` is the type of the hyper parameters optimization method\n", - "* `nIter` is the number of iterations of the hyper parameters method\n", - "* `metrics` is the list of the metrics and their arguments\n", - "* `viewsIndices` is 1d-array of the indices of the views used for classification\n", - "* `randomState` is a `numpy` RandomState object\n", - "* `labels` are the groud truth labels of the dataset" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The basic function analyzing results for all the classifiers looks like : " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "from ... import Metrics\n", - "from ...utils.MultiviewResultAnalysis import printMetricScore, getMetricsScores\n", - "\n", - "def execute(classifier, trainLabels,\n", - " testLabels, DATASET,\n", - " classificationKWARGS, classificationIndices,\n", - " LABELS_DICTIONARY, views, nbCores, times,\n", - " name, KFolds,\n", - " hyperParamSearch, nIter, metrics,\n", - " viewsIndices, randomState, labels):\n", - " CLASS_LABELS = labels\n", - " learningIndices, validationIndices, testIndicesMulticlass = classificationIndices\n", - "\n", - " metricModule = getattr(Metrics, metrics[0][0])\n", - " if metrics[0][1] is not None:\n", - " metricKWARGS = dict((index, metricConfig) for index, metricConfig in enumerate(metrics[0][1]))\n", - " else:\n", - " metricKWARGS = {}\n", - " scoreOnTrain = metricModule.score(CLASS_LABELS[learningIndices], CLASS_LABELS[learningIndices], **metricKWARGS)\n", - " scoreOnTest = metricModule.score(CLASS_LABELS[validationIndices], testLabels, **metricKWARGS)\n", - "\n", - " # To be modified to fit to your classifier \n", - " classifierConfigurationString = \"with weights : \"+ \", \".join(map(str, list(classifier.weights))) + \", and integer : \"+str(classifier.integer)\n", - " # Modify the name of the classifier in these strings\n", - " stringAnalysis = \"\\t\\tResult for Multiview classification with NMC \"+ \\\n", - " \"\\n\\n\" + metrics[0][0] + \" :\\n\\t-On Train : \" + str(scoreOnTrain) + \"\\n\\t-On Test : \" + str(\n", - " scoreOnTest) + \\\n", - " \"\\n\\nDataset info :\\n\\t-Database name : \" + name + \"\\n\\t-Labels : \" + \\\n", - " ', '.join(LABELS_DICTIONARY.values()) + \"\\n\\t-Views : \" + ', '.join(views) + \"\\n\\t-\" + str(\n", - " KFolds.n_splits) + \\\n", - " \" folds\\n\\nClassification configuration : \\n\\t-Algorithm used : NMC \" + classifierConfigurationString\n", - "\n", - " metricsScores = getMetricsScores(metrics, trainLabels, testLabels,\n", - " validationIndices, learningIndices, labels)\n", - " stringAnalysis += printMetricScore(metricsScores, metrics)\n", - "\n", - " imagesAnalysis = {}\n", - " return stringAnalysis, imagesAnalysis, metricsScores" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Once you have done this, your classifier is ready to be used by the platform, but you can add some description about your classifier in the analyzeResults file. " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Adding arguments to avoid hyper parameter optimization" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In order to be able to test a specific set of arguments on this platform, you need to add some lines in the argument parser located in the file `Code/MonoMultiViewClassifiers/utils/execution.py` in the `parseTheArgs` function. What you need to do is to add a group of arguments, allowing you to pass the hyper parameters in the command line :" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "groupNMC = parser.add_argument_group('New Multiview Classifier arguments')\n", - "groupNMC.add_argument('--NMC_weights', metavar='FLOAT', action='store', nargs=\"+\",\n", - " help='Determine the weights of NMC', type=float,\n", - " default=[])\n", - "groupNMC.add_argument('--NMC_integer', metavar='INT', action='store',\n", - " help='Determine the integer of NMC', type=int,\n", - " default=42)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In order for the platform to use these arguments, you need to modify the `getArgs` function of the file `NMCModule.py`. \n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def getArgs(args, benchmark, views, viewsIndices, randomState, directory, resultsMonoview, classificationIndices):\n", - " argumentsList = []\n", - " nbViews = len(views)\n", - " arguments = {\"CL_type\": \"NMC\",\n", - " \"views\": views,\n", - " \"NB_VIEW\": len(views),\n", - " \"viewsIndices\": viewsIndices,\n", - " \"NB_CLASS\": len(args.CL_classes),\n", - " \"LABELS_NAMES\": args.CL_classes,\n", - " \"NMCKWARGS\": {\"weights\":args.NMC_weights, # Modified to take the args into account\n", - " \"integer\":args.NMC_integer, # Modified to take the args into account\n", - " \"nbViews\":5}}\n", - " argumentsList.append(arguments)\n", - " return argumentsList" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 2", - "language": "python", - "name": "python2" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 2 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython2", - "version": "2.7.13" - } - }, - "nbformat": 4, - "nbformat_minor": 0 -} diff --git a/ipynb/ColorHistoTest.ipynb b/ipynb/ColorHistoTest.ipynb deleted file mode 100644 index ae2bb4ef7343d323d6a261de70500ae2c0cfe1cc..0000000000000000000000000000000000000000 --- a/ipynb/ColorHistoTest.ipynb +++ /dev/null @@ -1,211 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## COLOUR Histogramme Test" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "collapsed": false - }, - "outputs": [ - { - "data": { - "text/plain": [ - "-1" - ] - }, - "execution_count": 1, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "%matplotlib inline\n", - "# import the necessary packages\n", - "from matplotlib import pyplot as plt\n", - "import numpy as np\n", - "import argparse\n", - "import cv2\n", - "\n", - "\n", - "# Test Image\n", - "#file = '\"D://Organisation//07-Uni//06-Auslandsstudium//02-École Centrale de Marseille//04-3A//03-OMIS//02-Projet//03-jeux-de-données//Caltech//airplanes//image_0001.jpg'\"\n", - "#path = u\"D:\\\\Organisation\\\\07-Uni//06-Auslandsstudium//02-École Centrale de Marseille//image_0002.jpg\"\n", - "path = \"D:/Caltech//airplanes//image_0304.jpg\"\n", - "#path = \"./Test/vert.jpg\"\n", - "\n", - "# load the image and show it\n", - "#cv2.startWindowThread()\n", - "image = cv2.imread(path)\n", - "cv2.imshow(\"image\", image)\n", - "cv2.waitKey(0) \n" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "collapsed": false - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "flattened feature vector size: 48\n" - ] - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "<matplotlib.figure.Figure at 0xac200f0>" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# grab the image channels, initialize the tuple of colors,\n", - "# the figure and the flattened feature vector\n", - "chans = cv2.split(image)\n", - "colors = (\"b\", \"g\", \"r\")\n", - "plt.figure()\n", - "plt.title(\"'Flattened' Color Histogram\")\n", - "plt.xlabel(\"Bins\")\n", - "plt.ylabel(\"# of Pixels\")\n", - "features = []\n", - "\n", - "# loop over the image channels\n", - "for (chan, color) in zip(chans, colors):\n", - " # create a histogram for the current channel and\n", - " # concatenate the resulting histograms for each\n", - " # channel\n", - " hist = cv2.calcHist([chan], [0], None, [16], [0, 256])\n", - " features.extend(hist)\n", - " \n", - " # plot the histogram\n", - " plt.plot(hist, color = color)\n", - " plt.xlim([0, 16])\n", - " \n", - "# here we are simply showing the dimensionality of the\n", - "# flattened color histogram 256 bins for each channel\n", - "# x 3 channels = 768 total values -- in practice, we would\n", - "# normally not use 256 bins for each channel, a choice\n", - "# between 32-96 bins are normally used, but this tends\n", - "# to be application dependent\n", - "print \"flattened feature vector size: %d\" % (np.array(features).flatten().shape)" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": { - "collapsed": false - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[array([ 906.], dtype=float32), array([ 1061.], dtype=float32), array([ 1339.], dtype=float32), array([ 32064.], dtype=float32), array([ 40083.], dtype=float32), array([ 36.], dtype=float32), array([ 4.], dtype=float32), array([ 245.], dtype=float32), array([ 1488.], dtype=float32), array([ 2275.], dtype=float32), array([ 1658.], dtype=float32), array([ 1456.], dtype=float32), array([ 1140.], dtype=float32), array([ 910.], dtype=float32), array([ 785.], dtype=float32), array([ 870.], dtype=float32), array([ 1137.], dtype=float32), array([ 4761.], dtype=float32), array([ 66385.], dtype=float32), array([ 2850.], dtype=float32), array([ 52.], dtype=float32), array([ 12.], dtype=float32), array([ 28.], dtype=float32)]\n" - ] - } - ], - "source": [ - "print features[10:33]" - ] - }, - { - "cell_type": "code", - "execution_count": 45, - "metadata": { - "collapsed": false - }, - "outputs": [ - { - "data": { - "text/plain": [ - "<matplotlib.colorbar.Colorbar instance at 0x000000001C016608>" - ] - }, - "execution_count": 45, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAABpsAAAJBCAYAAAC57g2vAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJzs3XucZVV95/3PlwYRFdCWBLmXzrSjKGOACGY0whONQ9QA\nJoZLIiFKoglJIJdnEjFGQCdETMZrIrlxdyAQUSQJEpBJ+2AidkRQIjKC2ly6odHm0hhRupvf88fe\nVXW6qKredbqqTtWpz/v1Oq/etc7ae6+9q7r2t87aa+1UFZIkSZIkSZIkSVI/tht0AyRJkiRJkiRJ\nkrR42dkkSZIkSZIkSZKkvtnZJEmSJEmSJEmSpL7Z2SRJkiRJkiRJkqS+2dkkSZIkSZIkSZKkvtnZ\nJEmSJEmSJEmSpL5tP+gGSJI0V5LUfO+zqjLf+5QkSZoNZidJkqTuzE5bsrNJkjTUzhjSfUmSJM2F\nM4Z0X5IkSXPhjCHdVz/sbJIkDTUvdJIkSd2ZnSRJkrozO43zmU2SJEmSJEmSJEnqm51NkiRJkiRJ\nkiRJ6pujvCRJQ22HQTdAkiRpETE7SZIkdWd2GufIJkmSJEmSJEmSJPXNkU2SpKHmhU6SJKk7s5Mk\nSVJ3ZqdxjmySJEmSJEmSJElS3+x4kyQNNefOlSRJ6s7sJEmS1J3ZaZwjmyRJkiRJkiRJktQ3RzZJ\nkoaaFzpJkqTuzE6SJEndmZ3GObJJkiRJkiRJkiRJfbPjTZI01Jw7V5IkqTuzkyRJUndmp3GObJIk\nSZIkSZIkSVLf7GySJEmSJEmSJElS35xGT5I01LzQSZIkdWd2kiRJ6s7sNM6RTZIkSZIkSZIkSeqb\nHW+SpKHmgxolSZK6MztJkiR1Z3Ya58gmSZIkSZIkSZIk9c2RTZKkoeaFTpIkqTuzkyRJUndmp3GO\nbJIkSZIkSZIkSVLf7HiTJA01586VJEnqzuwkSZLUndlpnCObJEmSJEmSJEmS1DdHNkmShpp3mEiS\nJHVndpIkSerO7DTOkU2SJEmSJEmSJEnqmyObJElDzQudJElSd2YnSZKk7sxO4xzZJEmSJEmSJEmS\npL7Z2SRJkiRJkiRJkqS+OcpLkjTUfFCjJElSd2YnSZKk7sxO4xzZJEmSJEmSJEmSpL45skmSNNS8\n0EmSJHVndpIkSerO7DTOkU2SJEmSJEmSJEnqmx1vkqSh5ty5kiRJ3ZmdJEmSujM7jXNkkyRJkiRJ\nkiRJkvrmyCZJ0lDzQidJktSd2UmSJKk7s9M4RzZJkiRJkiRJkiSpb3a8SZKGmnPnSpIkdTfI7JRk\nH+Ai4IeBAv6qqj6cZDlwGbAfsBo4pqoebtc5DXgLsBk4paqubcsPBi4AngpcXVWntuU7tvs4CFgP\nHFtVd83XMUqSpOHi507jHNkkSZIkSZIWgo3Ab1fVi4CXAb+e5IXA24Hrqur5wPXt1yTZHzgW2B84\nAvhokrTbOgc4qapWACuSHNGWnwSsb8s/AJw9P4cmSZI03OxskiRJkiRJA1dV91fVLe3yd4GvAXsB\nRwIXttUuBI5ul48CLq2qjVW1GrgTODTJHsDOVbWqrXdRzzq927oCeNXcHZEkSdLS4TR6kqSh5oVO\nkiSpu4WSnZKMAAcCXwB2r6p17VvrgN3b5T2BG3tWu5emc2pjuzxqTVtO++89AFW1KckjSZZX1YNz\ncBiSJGnILZTstBA4skmSJEmSJC0YSZ5BM+ro1Kp6tPe9qiqa5zlJkiRpAbHjTZI01HxQoyRJUndz\nmZ1Wta/pJNmBpqPp4qq6si1el+Q5VXV/O0XeA235GmCfntX3phnRtKZdnlg+us6+wNok2wO7OqpJ\nkiT1y8+dxjmySZIkSZIkzblDgN/oeU2UJMC5wG1V9cGet64CTmyXTwSu7Ck/LslTkjwXWAGsqqr7\ngQ1JDm23eQLwqUm29Ubg+lk5OEmSpCXOkU2SpKHmhU6SJKm7AWenlwNvAr6S5Oa27DTgvcDlSU4C\nVgPHAFTVbUkuB24DNgEnt9PsAZwMXADsBFxdVde05ecCFye5A1gPHDfXByVJkoaXnzuNy3gOkyRp\nuCSpr8/j/p4PVFXmcZeSJEmzxuwkSZLUndlpS3a8SZKGmnPnSpIkdWd2kiRJ6s7sNM5nNkmSJEmS\nJEmSJKlvjmySJA017zCRJEnqzuwkSZLUndlpnCObJEmSJEmSJEmSFpEk5yVZl+TWnrL3JPlykluS\nXJ9kn7Z8JMljSW5uXx/tWefgJLcmuSPJh3rKd0xyWVt+Y5L9pmuPnU2SpKG2/Ty+JkqyT5J/TvLV\nJP+e5JQJ7/9ukieSLO8pO629iN+e5DU95bNy4ZckSZrOILOTJEnSYjPg7HQ+cMSEsvdV1Uuq6keA\nK4HTe967s6oObF8n95SfA5xUVSuAFUlGt3kSsL4t/wBw9nTnws4mSZLmzkbgt6vqRcDLgF9P8kJo\n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\npCVn2aAbIEmStIiYnSRJkrozO43xmU2SJEmSJEmSJEnqmyObJEnDzSudJElSd2YnSZKk7sxOYxzZ\nJEmSJEmSJEmSpL7Z7yZJGm5e6SRJkrozO0mSJHVndhrjyCZJkiRJkiRJkiT1zc4mSZIkSZIkSZIk\n9c1BXpKk4bZs0A2QJElaRMxOkiRJ3ZmdxjiySZIkSZIkSZIkSX1zZJMkabh5pZMkSerO7CRJktSd\n2WmMI5skSZIkSZIkSZLUN/vdJEnDzSudJElSd2YnSZKk7sxOY5bYqdg4w/q7zEkrts1Mj0GSJKlf\nM80dO/Wxj71mWH9NH/uQJElaiPr5jGemeWv5DOuvm2F9SZIaS6yzSZK05CwbdAMkSZIWEbOTJElS\nd2anMdvU2ZRkNbAB2AxsrKpDZqNRkiRJw8jsJEmS1I25SZKkxWVbRzYVcHhVPTgbjZEkadY5hlcL\ni9lJkrSwmZ20cJibJEkLn9lpzHazsI3MwjYkSZKWCrOTJElSN+YmSZIWiW3tbCrgM0m+mORXZqNB\nkiRJQ8zsJEmS1I25SZKkRWRbB3m9vKruS/JDwHVJbq+qG2ajYZIkzQqHM2thMTtJkhY2s5MWDnOT\nJGnhMzuN2aZTUVX3tf9+O8kngUOAngv/yp7aI+1LkrQ0rW5f0tJldpIkdbcas5OWsq3nJjA7SZLG\nrcbsNFh9dzYleRqwrKoeTfJ04DXAmVvWOnxb2iZJGiojbPnH32fnZ7feYaIFwuwkSZqZEcxOWqq6\n5SYwO0mSxo1gdhqsbTkVuwOfTDK6nf9dVdfOSqskSZKGj9lJkiSpG3OTJEmLTN+dTVX1LeBHZrEt\nkiTNvmWDboDUMDtJkhYFs5MWAHOTJGnRMDuN2W7QDZAkSZIkSZIkSdLi5YyC03qsj3WWz7D+xj72\nMVPzsQ9JWqC80knzaD6y00zrPzjD+pK0xJmdpG0w089f+vkPN9NsMx/Zyc+dJC1hZqcxjmySJEmS\nJEmSJElS3+x3kyQNN690kiRJ3ZmdJEmSujM7jXFkkyRJkiRJkiRJkvpmv5skabgtG3QDJEmSFhGz\nkyRJUndmpzGObJIkSZIkSZIkSVLf7GySJEmSJEmSJElaRJKcl2Rdklt7ypYnuS7J15Ncm+SZbflI\nkseS3Ny+PtqzzsFJbk1yR5IP9ZTvmOSytvzGJPtN1x47myRJw237eXxJkiQtdmYnSZKk7gabnc4H\n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\nnSRJkrozO41zZJMkSZIkSZIkSZL6Zr+bJGmobVo2n/dVPDGP+5IkSZp9ZidJkqTuzE7jHNkkSZIk\nSZIkSZKkvtnZJEmSJEmSJEmSpL45jZ4kaaht3n4+L3WPz+O+JEmSZp/ZSZIkqTuz0zhHNkmSJEmS\nJEmSJKlvjmySJA21zcuWDboJkiRJi4bZSZIkqTuz0zhHNkmSJEmSJEmSJKlvjmySJA21zXiHiSRJ\nUldmJ0mSpO7MTuPsbFoSHpth/Z1mWH8+fow2zsM+JEnS8Hmwj3V2meP6G2ZYX5IkqV/z8XnKTD9H\n2mGG9TfNsD74OZIkzT87myRJQ22Td5hIkiR1ZnaSJEnqzuw0zmc2SZIkSZIkSZIkqW9bHdmU5Dzg\ndcADVXVAW7YcuAzYD1gNHFNVD89hOyVJ6stmB/FqHpmbJEmLndlJ88nsJEla7MxO47qMbDofOGJC\n2duB66rq+cD17deSJElLnblJkiSpO7OTJElDYqudTVV1A/DQhOIjgQvb5QuBo2e5XZIkSYuOuUmS\nJKk7s5MkScOj3zFeu1fVunZ5HbD7LLVHkqRZtdkHNWrwzE2SpEXD7KQFwOwkSVo0zE7jtnlCwaqq\nJDX5uyt7lkfalyRpaVrdvqSla/rcBGYnSdK41ZidtNSZnSRJ3a3G7DRY/XY2rUvynKq6P8kewAOT\nVzu8z81LkobPCFv+8ffZedmrd5hoAeiYm8DsJEkaN4LZSUuU2UmS1IcRzE6DtdVnNk3hKuDEdvlE\n4MrZaY4kSdLQMTdJkiR1Z3aSJGkR2urIpiSXAocBuyW5B3gX8F7g8iQn0YxNO2YuGylJUr+8w0Tz\nydwkSVrszE6aT2YnSdJiZ3Yat9XOpqo6foq3Xj3LbZEkSVrUzE2SJEndmZ0kSRoe/T6zSZKkRWGT\nd5hIkiR1ZnaSJEnqzuw0zs4mTeKxGdbfoY99zPWP3sY53r4kSRpeG2ZYf5cZ1t9phvVh5vlMkiSp\nH/Pxecp8fBw508+q/BxJ0uKU5FTgl4EAf11VH0qyHLgM2I92StqqeritfxrwFmAzcEpVXduWHwxc\nADwVuLqqTp1pW+xskiQNtc1e6iRJkjozO0mSJHU3yOyU5MU0HU0vpek1vybJPwBvA66rqvcl+X3g\n7cDbk+wPHAvsD+wFfCbJiqoq4BzgpKpaleTqJEdU1TUzac92s3dokiRJkiRJkiRJmgcvAL5QVd+v\n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\nf95/vrv/pKqeneR3kvwfSS7NgnzWVdU5Y4Y3avyjrX2jRhaL7CQ7ZQv/fp2W7CQ7RXba0p+1MuOU\nJgAADPRJREFU7MQi267ZaTvkpkR2iuy01Nlpu+amRHaa2BbPTrOc2fQDSR7u7pXuPpTkl5K8eobv\nP29b9h/BydDdH0/yh8c0X5rktmH7tox+SS6VpznuZIk/7+5+vLs/NWx/M8nnk5yTJf+8j3PcyXJ/\n3n8ybJ6W5FkZ/XtfrM/aFSYsLtlpiclOf8bSft6yk+yURfusZScW13bOTkv7e3WV7PRnLO1nvh2z\n03bNTYnstGzZaZaDTeckeXTd8/1Z+49m2XWSj1TVJ6vqH8y7mBk6s7sPDtsHk5w5z2Jm7K1V9emq\numXLT/U8AVW1I8nLknwi2+jzXnfc9w9NS/t5V9UpVfWpjD7T3+7uz2UbfdYwZ7KT7LSdfr8u7Xfp\nerKT7JQl/6xhzrZrdtquuSnZ3r9fl/a7dL3tmJ22U25KZKdlM8vBphnOJ9tyfrC7X5bkR5P8w2EK\n7LbSo/Uat8u/gZuSnJ/koiSPJXnXfMvZHMO03l9Nck13/9H615b58x6O+1cyOu5vZsk/7+7+dndf\nlOTcJH+jql5xzOtb/7M+NMMHnFxb+7+tzSU7LcLv15Nnqb9LV8lOstPw+tb/rGUnFtfW/m9r82z7\n3JQsyO/Xk2epv0tXbcfstN1yUyI7LVt2muVg04Ek5617fl5GV5ksve5+bPjzK0l+LaOp3dvBwWHN\n0VTVWUmemHM9M9HdT/Qgyc1Zws+7qk7N6Av/A939oaF56T/vdcf9C6vHvR0+7yTp7m8k+Y0kF2cb\nfNawRchOstO2+P26Hb5LZSfZadk/a9gitmV22sa5Kdmmv1+3w3fpdsxO2zk3JbLTspjlYNMnk+ys\nqh1VdVqS1ya5a4bvPxdV9eer6oxh+zuSvCrJQ/OtambuSvLGYfuNST50nL5LY/gluOo1WbLPu6oq\nyS1J9nX3u9e9tNSf99Md9zJ/3lX1gtUp2lV1epIfSfJgFu2zPjLDB5xcspPstLV/v54ky/xdmshO\nkZ1kJ9mJ2dl22Wmb56Zk0X6/niTL/F2abM/stB1zUyI7LWN2qtHA6IzerOpHk7w7o5t93dLdPzOz\nN5+Tqjo/oytLkuTZSX5xGY+7qu5I8vIkL8hoLc1/nuTXk9yZ5LuTrCS5vLu/Pq8aN8MGx/32JLsz\nmt7aSb6c5C3r1hldeFX1Q0n+Q5LPZG0a63VJ9maJP++nOe63JXl9lvTzrqoLM7oR4ynD4wPd/S+r\n6vlZkM+6qjo1w9nWXenupb55J7MlO8lOW/X367RkJ9kpstNKtvBnLTux6LZbdtouuSmRnSI7LXV2\n2o65KZGdprLFs9NMB5sAYJZ86QMAjE92AgAYn+z0VLNcRg8AZq9n+AAAWHRzzE5VdV5V/XZVfa6q\nPltVVw/t76iq/VX14PD40XX7XFdVX6qqL1TVq9a1X1xVDw2vvefk/QUBAKzjvNNRBpsAYJNU1XOq\n6hNV9amq2ldVPzO0P7+q7quqL1bVvatrFA+vOWECAGxXh5L8k+7+niR/Pck/rKqXZnR65cbuftnw\n+M0kqaoLMrovzwVJLkny88N9L5LkpiRXdvfOjO7jc8msDwYAYDsx2AQAm6S7/zTJK7r7oiTfm+QV\nw1rM1ya5r7tfkuSjw3MnTACAba27H+/uTw3b30zy+STnDC9vtGTMq5Pc0d2HunslycNJdg03VD+j\nu/cO/W5PctmmFg8AsM0ZbAKATdTdfzJsnpbRjYr/MMmlGd0EM8Ofqyc/nDABAEhSVTuSvCzJ/UPT\nW6vq01V1y7pZ4Wcn2b9ut/0ZDU4d234ga4NWAABsgmfPuwAAWGZVdUqS30vyl5Pc1N2fq6ozu/vg\n0OVgkjOH7bOzdkIlWTthcihOmAAAC2/P8Di+qvrOJL+S5Jru/mZV3ZTkp4aXfzrJu5JcuTk1AgAw\nDYNNALCJuvvbSS6qqucm+a2qesUxr3dVLcBtHgEATtTu4bHq+j/To6pOTfKrSX6huz+UJN39xLrX\nb07y4eHpgSTnrdv93Iwu0DkwbK9vP3Ci1QMA8PQMNgHA1PZknKtzk6S7v1FVv5Hk4iQHq+pF3f34\nsETe6gkUJ0wAgG1ruFflLUn2dfe717Wf1d2PDU9fk+ShYfuuJB+sqhszmvW9M8ne4WKeJ6tqV5K9\nSa5I8t5ZHQcAwHZU3S6mBmA5jWYMzfJ7rtLdR29eXVUvSHK4u79eVacn+a2MLuH9n5J8tbvfWVXX\nJnled19bVRck+WCSH8johMlHkvy3wwmTTyS5OqMTJr+R5L3dfc8MDw4AWHJbIDv9UJL/kOQzWSvk\nbUlen+Sioe3LSd6yuiRxVb0tyZuTHM5o2b3fGtovTnJrktOT3N3dV8/iiACA7WPe2WmrMdgEwNIa\nfen/1xm+42nHnjC5MMltSU4ZHh/o7n9ZVc9PcmeS706ykuTy7v76sI8TJgDAXMw7OwEALJJ5Z6eq\nel+Sv5Xkie6+cGh7R5K/n+QrQ7e3dfdvDq9dl9E5pyNJru7ue4f21XNOz8nonNM101RnsAmApTXv\nL30AgEUiOwEAjG/e2amqfjjJN5Pcvm6w6e1J/qi7bzym1tXVdL4/a6vp7BxW09mb5B91996qujtT\nrqbjnk0ALLnD8y4AAGCByE4AAOObX3bq7o9X1Y4NXtroYp5XJ7mjuw8lWamqh5PsqqpHkpzR3XuH\nfrcnuSzJxINNp0y6AwAAAAAAAFvSW6vq01V1S1U9b2g7O8n+dX32ZzTD6dj2A0P7xAw2AQAAAAAA\nLL6bkpyf5KIkjyV516ze2DJ6ACy5Q/MuAABggchOAADj28zs9PEkvzPRHt39xOp2Vd2c5MPD0wNJ\nzlvX9dyMZjQdGLbXtx+YolgzmwAAAAAAALaWH05y3brHM6uqs9Y9fU2Sh4btu5K8rqpOq6rzk+xM\nsre7H0/yZFXtqqpKckWSD01TrZlNACw5N7kGABif7AQAML75ZaequiPJy5O8oKoeTfL2JLur6qIk\nneTLSd6SJN29r6ruTLIvo6Kv6u4eftRVSW5NcnqSu7v7nqnqWft5ALBcqqqTr87wHf9iurtm+IYA\nACeN7AQAMD7Z6anMbAJgybnvAADA+GQnAIDxyU6r3LMJAAAAAACAqZnZBMCSc4UJAMD4ZCcAgPHJ\nTqvMbAIAAAAAAGBqZjYBsOQOz7sAAIAFIjsBAIxPdlplZhMAAAAAAABTM9gEAAAAAADA1CyjB8CS\nc6NGAIDxyU4AAOOTnVaZ2QQAAAAAAMDUzGwCYMm5USMAwPhkJwCA8f3/7d0xihVBFIXhU467kNmG\nLkADM2P3YG76FmFqbmzmFnQBKhoIOm7CgTaQggFFLhce3a/4vuxF3eHP1Jxq7TRZNgEAAAAAANBm\n2QTA4tydCwBQp50AAOq002TZBAAAAAAAQJtlEwCLc3cuAECddgIAqNNOk2UTAAAAAAAAbZZNACzO\n3bkAAHXaCQCgTjtNlk0AAAAAAAC0WTYBsDh35wIA1GknAIA67TRZNgEAAAAAANDmsAkAAAAAAIA2\n1+gBsDgfagQAqNNOAAB12mmybAIAAAAAAKDNsgmAxflQIwBAnXYCAKjTTpNlEwAAAAAAAG2WTQAs\nzt25AAB12gkAoE47TZZNAAAAAAAAtFk2AbA4d+cCANRpJwCAOu00WTYBAAAAAADQZtkEwOLcnQsA\nUKedAADqtNNk2QQAAAAAAECbwyYAAAAAAADaXKMHwOLMmQEA6rQTAECddposmwAAAAAAAGizbAJg\ncbd7vwAAwAXRTgAAddppsmwCAAAAAACgzbIJgMW5OxcAoE47AQDUaafJsgkAAAAAAIA2yyYAFufu\nXACAOu0EAFCnnSbLJgAAAAAAANosmwBYnLtzAQDqtBMAQJ12miybAAAAAAAAaLNsAmBx7s4FAKjT\nTgAAddppsmwCAAAAAACgzWETAAAAAAAAba7RA2BxPtQIAFCnnQAA6rTTZNkEAAAAAABAm2UTAIvz\noUYAgDrtBABQp50myyYAOKMxxtMxxqcxxpcxxsu93wcA4Mi0EwBA3ZHaybIJgMXtd3fuGOMqyask\nT5LcJPkwxni7bdvH3V4KAOC/tBMAQJ12miybAOB8Hib5um3bt23bfiV5k+TZzu8EAHBU2gkAoO5Q\n7WTZBMDidr0790GS73d+/0jyaKd3AQAo0E4AAHXaabJsAoDz2fZ+AQCAC6KdAADqDtVOlk0ALO60\n58Nvklzf+X2dP/9lAgBwUKc9H66dAIALc9rz4Ydqp7Fthzr8AoBljDHuJ/mc5HGSn0neJ3nuI9cA\nAH/TTgAAdUdrJ8smADiTbdtuxxgvkrxLcpXktT+WAAD8m3YCAKg7WjtZNgEAAAAAANB2b+8XAAAA\nAAAA4HI5bAIAAAAAAKDNYRMAAAAAAABtDpsAAAAAAABoc9gEAAAAAABAm8MmAAAAAAAA2hw2AQAA\nAAAA0OawCQAAAAAAgLbfXMH9m9fRIFkAAAAASUVORK5CYII=\n", - "text/plain": [ - "<matplotlib.figure.Figure at 0x18c60d68>" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# let's move on to 2D histograms -- I am reducing the\n", - "# number of bins in the histogram from 256 to 32 so we\n", - "# can better visualize the results\n", - "fig = plt.figure(figsize=(30,10))\n", - "\n", - "# plot a 2D color histogram for green and blue\n", - "ax = fig.add_subplot(131)\n", - "hist = cv2.calcHist([chans[1], chans[0]], [0, 1], None, [32, 32], [0, 256, 0, 256])\n", - "p = ax.imshow(hist, interpolation = \"nearest\")\n", - "ax.set_title(\"2D Color Histogram for Green and Blue\")\n", - "plt.colorbar(p)\n", - " \n", - "# plot a 2D color histogram for green and red\n", - "ax = fig.add_subplot(132)\n", - "hist = cv2.calcHist([chans[1], chans[2]], [0, 1], None, [32, 32], [0, 256, 0, 256])\n", - "p = ax.imshow(hist, interpolation = \"nearest\")\n", - "ax.set_title(\"2D Color Histogram for Green and Red\")\n", - "plt.colorbar(p)\n", - " \n", - "# plot a 2D color histogram for blue and red\n", - "ax = fig.add_subplot(133)\n", - "hist = cv2.calcHist([chans[0], chans[2]], [0, 1], None, [32, 32], [0, 256, 0, 256])\n", - "p = ax.imshow(hist, interpolation = \"nearest\")\n", - "ax.set_title(\"2D Color Histogram for Blue and Red\")\n", - "plt.colorbar(p)\n", - "\n", - "\n", - " \n", - "# finally, let's examine the dimensionality of one of\n", - "# the 2D histograms\n", - "#print \"2D histogram shape: %s, with %d values\" % (\n", - "#\thist.shape, hist.flatten().shape[0])" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 2", - "language": "python", - "name": "python2" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 2 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython2", - "version": "2.7.10" - } - }, - "nbformat": 4, - "nbformat_minor": 0 -} diff --git a/ipynb/Feature Extraction - ColorHistogram - NormDistibution.ipynb b/ipynb/Feature Extraction - ColorHistogram - NormDistibution.ipynb deleted file mode 100644 index 34eaf78d98101e8e9d94a3e4a8ab4ed9c38650ef..0000000000000000000000000000000000000000 --- a/ipynb/Feature Extraction - ColorHistogram - NormDistibution.ipynb +++ /dev/null @@ -1,261 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Code to Extract ColorHistograms for Database" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Author: Nikolas Hülsmann\n", - "#### Date: 2015-11-22" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Functions for Extract Data\n", - "\n", - "### Function to iterate through given directory and return images paths and classLabels" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "def imgCrawl(path): #path to 'highest' folder\n", - " rootdir = path\n", - " df = pd.DataFrame()\n", - " \n", - " for subdir, dirs, files in os.walk(rootdir): # loop through subdirectories\n", - " for file in files:\n", - " pathOfFile = os.path.join(subdir, file) #path of file\n", - " head, classLabel = os.path.split(os.path.split(pathOfFile)[0]) # get directoryname of file as classLabel\n", - " df = df.append({'classLabel': classLabel, 'pathOfFile': pathOfFile}, ignore_index=True) \n", - " \n", - " return df" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Function to determine Class-Labels with Integer representation" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# function to determine Class-labels and return Series\n", - "def getClassLabels(path):\n", - " data = os.listdir(path) # listdir returns all subdirectories\n", - " index = range(0,len(data))\n", - " \n", - " return pd.Series(data,index)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Function to calculate the ColorHistogram for given Images " - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "#### Calculate ColorHistograms for all images\n", - "\n", - "# path to higehst folder\n", - "# dfImages: Dataframe with paths to all images - use function imgCrawl\n", - "# sClassLabel: Series with ClassLabels - use function getClassLabels\n", - "def calcColorHisto(path_, dfImages_, sClassLabels_):\n", - " # Initialize function\n", - " df = pd.DataFrame()\n", - " path =path_\n", - " npImages = dfImages_.values\n", - " sClassLabels = sClassLabels_\n", - "\n", - " ## algo\n", - " for images in npImages:\n", - " image = cv2.imread(images[1])\n", - " \n", - " # Image Size for Normalization\n", - " height, width, channels = image.shape\n", - " img_size = height * width\n", - " \n", - " # Split into color chanels rgb\n", - " chans = cv2.split(image)\n", - " colors = (\"b\", \"g\", \"r\")\n", - " \n", - " features = []\n", - " i = 1\n", - "\n", - " # loop over the image channels\n", - " for chan in chans:\n", - " # Calculate Color Histogram - 16 bins cf. paper (test with 64 has shown that die result is similair in score)\n", - " hist = cv2.calcHist([chan], [0], None, [16], [0, 256])\n", - "\n", - " print i\n", - " i=i+1\n", - " # to get raw values\n", - " hist = hist[:,0]\n", - " \n", - " # Normalize to a Distrubution from 0 to 1 throug calculating for each color channel (red/blue/green): \n", - " # (number of pixels in bin)/(pixel size of image)\n", - " hist[:] = [x / img_size for x in hist]\n", - "\n", - " # Normalize with MinMax from 0 to 1 -> feature scaling\n", - " #cv2.normalize(hist, hist, 0, 1, cv2.NORM_MINMAX)\n", - " \n", - " \n", - " features.extend(hist)\n", - "\n", - " # assign integer label for dataframe\n", - " classLabel = sClassLabels[sClassLabels == images[0]].index[0]\n", - "\n", - " # append features to df\n", - " df = df.append({'classLabel': classLabel, 'ColHisto': features}, ignore_index=True) \n", - " \n", - " return df" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Function to export calculated Data to csv " - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "#### Export ColorHistogram to csv\n", - "def exportToCSV(pandasSorDF, filename):\n", - " #filename = datetime.datetime.now().strftime(\"%Y_%m_%d\") + \"-ColorHistogram\"\n", - " path = os.getcwdu() + \"\\\\\" + filename\n", - " \n", - " if os.path.isfile(path + \".csv\"):\n", - " for i in range(1,20):\n", - " testFileName = filename + \"-\" + str(i) + \".csv\"\n", - " if os.path.isfile(os.getcwdu() + \"\\\\\" + testFileName)!=True:\n", - " pandasSorDF.to_csv(testFileName)\n", - " break\n", - "\n", - " else:\n", - " pandasSorDF.to_csv(filename + \".csv\")\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Main Programm\n" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Imports\n", - "import os # for iteration throug directories\n", - "import pandas as pd # for Series and DataFrames\n", - "import cv2 # for OpenCV \n", - "import datetime # for TimeStamp in CSVFile" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": { - "collapsed": false - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1\n", - "2\n", - "3\n" - ] - }, - { - "ename": "IndexError", - "evalue": "index 0 is out of bounds for axis 0 with size 0", - "traceback": [ - "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[1;31mIndexError\u001b[0m Traceback (most recent call last)", - "\u001b[1;32m<ipython-input-10-543bf34003a8>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m()\u001b[0m\n\u001b[0;32m 3\u001b[0m \u001b[0mdfImages\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mimgCrawl\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mpath\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 4\u001b[0m \u001b[0msClassLabels\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mgetClassLabels\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mpath\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 5\u001b[1;33m \u001b[0mdfColorHistogram\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mcalcColorHisto\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mpath\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mdfImages\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0msClassLabels\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 6\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 7\u001b[0m \u001b[0mfileNameColorHis\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mdatetime\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mdatetime\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mnow\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mstrftime\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m\"%Y_%m_%d\"\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;33m+\u001b[0m \u001b[1;34m\"-Features\"\u001b[0m \u001b[1;33m+\u001b[0m\u001b[1;34m\"-ColorHistogram\"\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", - "\u001b[1;32m<ipython-input-9-3a59b70a518a>\u001b[0m in \u001b[0;36mcalcColorHisto\u001b[1;34m(path_, dfImages_, sClassLabels_)\u001b[0m\n\u001b[0;32m 47\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 48\u001b[0m \u001b[1;31m# assign integer label for dataframe\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 49\u001b[1;33m \u001b[0mclassLabel\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0msClassLabels\u001b[0m\u001b[1;33m[\u001b[0m\u001b[0msClassLabels\u001b[0m \u001b[1;33m==\u001b[0m \u001b[0mimages\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mindex\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 50\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 51\u001b[0m \u001b[1;31m# append features to df\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", - "\u001b[1;32mD:\\Programme\\Anaconda\\lib\\site-packages\\pandas\\core\\index.pyc\u001b[0m in \u001b[0;36m__getitem__\u001b[1;34m(self, key)\u001b[0m\n\u001b[0;32m 1074\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 1075\u001b[0m \u001b[1;32mif\u001b[0m \u001b[0mnp\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0misscalar\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mkey\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m-> 1076\u001b[1;33m \u001b[1;32mreturn\u001b[0m \u001b[0mgetitem\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mkey\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 1077\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 1078\u001b[0m \u001b[1;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mkey\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mslice\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", - "\u001b[1;31mIndexError\u001b[0m: index 0 is out of bounds for axis 0 with size 0" - ], - "output_type": "error" - } - ], - "source": [ - "#### Calculate Color Histogram\n", - "path ='D:\\CaltechMini'\n", - "dfImages = imgCrawl(path)\n", - "sClassLabels = getClassLabels(path)\n", - "dfColorHistogram = calcColorHisto(path, dfImages, sClassLabels)\n", - "\n", - "fileNameColorHis = datetime.datetime.now().strftime(\"%Y_%m_%d\") + \"-Features\" +\"-ColorHistogram\"\n", - "exportToCSV(dfColorHistogram, fileNameColorHis)\n", - "\n", - "fileNameClassLabels = datetime.datetime.now().strftime(\"%Y_%m_%d\") + \"-ClassLabels\" + \"-Caltech\"\n", - "exportToCSV(sClassLabels, fileNameClassLabels)\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 2", - "language": "python", - "name": "python2" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 2 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython2", - "version": "2.7.10" - } - }, - "nbformat": 4, - "nbformat_minor": 0 -} diff --git a/ipynb/Feature Extraction - ColorHistogram.ipynb b/ipynb/Feature Extraction - ColorHistogram.ipynb deleted file mode 100644 index a2ac1f39ffcd4547c6bd5e57c9ab8d434fcda495..0000000000000000000000000000000000000000 --- a/ipynb/Feature Extraction - ColorHistogram.ipynb +++ /dev/null @@ -1,222 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Code to Extract ColorHistograms for Database" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Author: Nikolas Hülsmann\n", - "#### Date: 2015-11-22" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Functions for Extract Data\n", - "\n", - "### Function to iterate through given directory and return images paths and classLabels" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "def imgCrawl(path): #path to 'highest' folder\n", - " rootdir = path\n", - " df = pd.DataFrame()\n", - " \n", - " for subdir, dirs, files in os.walk(rootdir): # loop through subdirectories\n", - " for file in files:\n", - " pathOfFile = os.path.join(subdir, file) #path of file\n", - " head, classLabel = os.path.split(os.path.split(pathOfFile)[0]) # get directoryname of file as classLabel\n", - " df = df.append({'classLabel': classLabel, 'pathOfFile': pathOfFile}, ignore_index=True) \n", - " \n", - " return df" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Function to determine Class-Labels with Integer representation" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# function to determine Class-labels and return Series\n", - "def getClassLabels(path):\n", - " data = os.listdir(path) # listdir returns all subdirectories\n", - " index = range(0,len(data))\n", - " \n", - " return pd.Series(data,index)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Function to calculate the ColorHistogram for given Images " - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "#### Calculate ColorHistograms for all images\n", - "\n", - "# path to higehst folder\n", - "# dfImages: Dataframe with paths to all images - use function imgCrawl\n", - "# sClassLabel: Series with ClassLabels - use function getClassLabels\n", - "def calcColorHisto(path_, dfImages_, sClassLabels_):\n", - " # Initialize function\n", - " df = pd.DataFrame()\n", - " path =path_\n", - " npImages = dfImages_.values\n", - " sClassLabels = sClassLabels_\n", - "\n", - " ## algo\n", - " for images in npImages:\n", - " image = cv2.imread(images[1])\n", - " chans = cv2.split(image) # Split into color chanels rgb\n", - " colors = (\"b\", \"g\", \"r\")\n", - " features = []\n", - "\n", - " # loop over the image channels\n", - " for (chan, color) in zip(chans, colors):\n", - " # Calculate Color Histogram - 16 bins cf. paper\n", - " hist = cv2.calcHist([chan], [0], None, [16], [0, 256])\n", - "\n", - " # to get raw values\n", - " hist = hist[:,0]\n", - "\n", - " # Normalize with MinMax from 0 to 1 -> feature scaling\n", - " cv2.normalize(hist, hist, 0, 1, cv2.NORM_MINMAX)\n", - " features.extend(hist)\n", - "\n", - " # assign integer label for dataframe\n", - " classLabel = sClassLabels[sClassLabels == images[0]].index[0]\n", - "\n", - " # append features to df\n", - " df = df.append({'classLabel': classLabel, 'ColHisto': features}, ignore_index=True) \n", - " \n", - " return df" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Function to export calculated Data to csv " - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "#### Export ColorHistogram to csv\n", - "def exportToCSV(pandasSorDF, filename):\n", - " #filename = datetime.datetime.now().strftime(\"%Y_%m_%d\") + \"-ColorHistogram\"\n", - " path = os.getcwdu() + \"\\\\\" + filename\n", - " \n", - " if os.path.isfile(path + \".csv\"):\n", - " for i in range(1,20):\n", - " testFileName = filename + \"-\" + str(i) + \".csv\"\n", - " if os.path.isfile(os.getcwdu() + \"\\\\\" + testFileName)!=True:\n", - " pandasSorDF.to_csv(testFileName)\n", - " break\n", - "\n", - " else:\n", - " pandasSorDF.to_csv(filename + \".csv\")\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Main Programm\n" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Imports\n", - "import os # for iteration throug directories\n", - "import pandas as pd # for Series and DataFrames\n", - "import cv2 # for OpenCV \n", - "import datetime # for TimeStamp in CSVFile" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "#### Calculate Color Histogram\n", - "path ='D:\\Caltech'\n", - "dfImages = imgCrawl(path)\n", - "sClassLabels = getClassLabels(path)\n", - "dfColorHistogram = calcColorHisto(path, dfImages, sClassLabels)\n", - "\n", - "fileNameColorHis = datetime.datetime.now().strftime(\"%Y_%m_%d\") + \"-Features\" +\"-ColorHistogram\"\n", - "exportToCSV(dfColorHistogram, fileNameColorHis)\n", - "\n", - "fileNameClassLabels = datetime.datetime.now().strftime(\"%Y_%m_%d\") + \"-ClassLabels\" + \"-Caltech\"\n", - "exportToCSV(sClassLabels, fileNameClassLabels)\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 2", - "language": "python", - "name": "python2" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 2 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython2", - "version": "2.7.10" - } - }, - "nbformat": 4, - "nbformat_minor": 0 -} diff --git a/ipynb/FeatureExtraction-All.ipynb b/ipynb/FeatureExtraction-All.ipynb deleted file mode 100644 index 34e8e06ce8f26758b19fe9dd7232c4659903e4fe..0000000000000000000000000000000000000000 --- a/ipynb/FeatureExtraction-All.ipynb +++ /dev/null @@ -1,707 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Code to Extract all Features from Database" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Author: Nikolas Hülsmann\n", - "#### Date: 2015-12-06" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Functions for Extract Data\n", - "\n", - "### Function to iterate through given directory and return images paths and classLabels" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "def imgCrawl(path, sClassLabels): #path to 'highest' folder\n", - " rootdir = path\n", - " df = pd.DataFrame()\n", - " \n", - " for subdir, dirs, files in os.walk(rootdir): # loop through subdirectories\n", - " for file in files:\n", - " pathOfFile = os.path.join(subdir, file) #path of file\n", - " head, classLabel = os.path.split(os.path.split(pathOfFile)[0]) # get directoryname of file as classLabel\n", - " \n", - " # assign integer label for dataframe\n", - " classLabel = sClassLabels[sClassLabels == classLabel].index[0]\n", - " df = df.append({'classLabel': classLabel, 'pathOfFile': pathOfFile}, ignore_index=True) \n", - " \n", - " return df" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Function to determine Class-Labels with Integer representation" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# function to determine Class-labels and return Series\n", - "def getClassLabels(path):\n", - " data = os.listdir(path) # listdir returns all subdirectories\n", - " index = range(0,len(data))\n", - " \n", - " return pd.Series(data,index)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Function to calculate the RGBColorHistogram for given Images " - ] - }, - { - "cell_type": "code", - "execution_count": 44, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "#### Calculate RGBColorHistograms for all images\n", - "\n", - "### Points to improve: \n", - "\n", - "# dfImages: Dataframe with paths to all images - use function imgCrawl\n", - "# numberOfBins_: Number of bins Histogram\n", - "def calcRGBColorHisto(dfImages_, numberOfBins_):\n", - " # Initialize function\n", - "\n", - " npImages = dfImages_.values\n", - " numberOfBins = numberOfBins_\n", - " npColorHist = np.zeros((len(npImages), numberOfBins*3), \"float32\")\n", - " i=0\n", - " \n", - " ## algo\n", - " for images in npImages:\n", - " image = cv2.imread(images[1])\n", - " \n", - " # Image Size for Normalization\n", - " #height, width, channels = image.shape\n", - " #img_size = height * width\n", - " \n", - " # Split into color chanels rgb\n", - " chans = cv2.split(image)\n", - " colors = (\"b\", \"g\", \"r\")\n", - " \n", - " histogram = []\n", - "\n", - " ########### Feature Color Histogram (cf. http://docs.opencv.org/2.4/doc/tutorials/imgproc/histograms/histogram_calculation/histogram_calculation.html) # loop over the image channels\n", - " for (chan, color) in zip(chans, colors): \n", - " \n", - " # Calculate Color Histogram - 16 bins cf. paper (test with 64 has shown that die result is similair in score)\n", - " # Seperates the intesity for each color from 0 to 256, and creates 16 bins of same size: 0-15, 16-31, .. , 241-256\n", - " hist = cv2.calcHist([chan], [0], None, [numberOfBins], [0, 256])\n", - "\n", - " # to get raw values\n", - " hist = hist[:,0]\n", - " \n", - " # Normalize to a Distrubution from 0 to 1 throug calculating for each color channel (red/blue/green): \n", - " # (number of pixels in bin)/(sum of pixels in histogram)\n", - " #hist[:] = [x / img_size for x in hist]\n", - " sumHist = sum(hist)\n", - " if(sumHist>0):\n", - " hist[:] = [x / sumHist for x in hist]\n", - " else:\n", - " print colored(\"WARNING NORMELIZATION: sumHIST is zero (0)\", 'red')\n", - " print colored(\"image: \" + images[1] + \"\\n\", 'red')\n", - " \n", - "\n", - " # Normalize with MinMax from 0 to 1 -> feature scaling\n", - " #cv2.normalize(hist, hist, 0, 1, cv2.NORM_MINMAX)\n", - " \n", - " histogram.extend(hist)\n", - "\n", - " # append features_colHist to npColorHist\n", - " npColorHist[i] = histogram\n", - " i = i+1\n", - " \n", - " \n", - " return npColorHist" - ] - }, - { - "cell_type": "code", - "execution_count": 43, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "#### Calculate HSVColorHistograms for all images\n", - "\n", - "### Points to improve: \n", - "\n", - "# dfImages: Dataframe with paths to all images - use function imgCrawl\n", - "# histSize: Number of bins in Histogram (differenz for each channel H,S,V)\n", - "def calcHSVColorHisto(dfImages_, histSize_):\n", - " # Initialize function\n", - " npImages = dfImages_.values\n", - " histSize = histSize_\n", - " npColorHist = np.zeros((len(npImages), int(histSize[0]+histSize[1]+histSize[2])), \"float32\")\n", - " i=0\n", - "\n", - " h_ranges = [ 0, 180 ]\n", - " s_ranges = [ 0, 256 ]\n", - " v_ranges = [ 0, 256 ]\n", - "\n", - " ranges = []\n", - "\n", - " ranges.append(h_ranges)\n", - " ranges.append(s_ranges)\n", - " ranges.append(v_ranges)\n", - "\n", - " channels = [ 0, 1, 2]\n", - " \n", - " histogram = []\n", - " \n", - " ## algo\n", - " for images in npImages:\n", - " image = cv2.imread(images[1])\n", - " hsv = cv2.cvtColor(img,cv2.COLOR_BGR2HSV)\n", - " \n", - " \n", - " # Split into color chanels rgb\n", - " chans = cv2.split(image)\n", - " colors = (\"H\", \"S\", \"V\")\n", - " \n", - " histogram = []\n", - " \n", - " ########### Feature Color Histogram (cf. http://docs.opencv.org/2.4/doc/tutorials/imgproc/histograms/histogram_calculation/histogram_calculation.html) # loop over the image channels\n", - " for (chan, color, binsize, range_chan) in zip(chans, colors, histSize, ranges): \n", - " hist = cv2.calcHist([chan], [0], None, [binsize], range_chan )\n", - " \n", - " # to get raw values\n", - " hist = hist[:,0]\n", - " \n", - " \n", - " # Normalize to a Distrubution from 0 to 1 throug calculating for each color channel (H/S/V): \n", - " # (number of pixels in bin)/(sum of pixels in histogram)\n", - " #hist[:] = [x / img_size for x in hist]\n", - " sumHist = sum(hist)\n", - " if(sumHist>0):\n", - " hist[:] = [x / sumHist for x in hist]\n", - " else:\n", - " print colored(\"WARNING NORMELIZATION: sumHIST is zero (0)\", 'red')\n", - " print colored(\"image: \" + images[1] + \"\\n\", 'red')\n", - " \n", - " histogram.extend(hist)\n", - " \n", - " \n", - " # append features_colHist to npColorHist\n", - " npColorHist[i] = histogram\n", - " i = i+1\n", - " \n", - " \n", - " return npColorHist\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Function to calculate Surf Histogram" - ] - }, - { - "cell_type": "code", - "execution_count": 42, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "################# FEATURE SURF (cf. http://docs.opencv.org/3.0-beta/doc/py_tutorials/py_feature2d/py_surf_intro/py_surf_intro.html#surf)\n", - "# API cf. http://docs.opencv.org/2.4/modules/nonfree/doc/feature_detection.html\n", - "\n", - "#### Calculate Histogramm of SURF Descripteurs with Bag Of Words appraoch for all images\n", - "\n", - "### Points to improve: \n", - "# - use spatial histogram: http://www.di.ens.fr/willow/events/cvml2011/materials/practical-classification/\n", - "# - change function: parameter how many K clustes/feature length (in regard of overfitting)\n", - "\n", - "\n", - "# path to higehst folder\n", - "# dfImages: Dataframe with paths to all images - use function imgCrawl\n", - "# k: number of K-Cluster -> length of feature vector\n", - "def calcSurfHisto(dfImages_, k_):\n", - " \n", - " # Initialize function\n", - " npImages = dfImages_.values\n", - " k = k_\n", - " \n", - " # List where all the descriptors are stored\n", - " des_list = []\n", - " \n", - " #### Feature Detection and Description (Surf): \n", - " # Detect (localize) for each image the keypoints (Points of Interest in an image - Surf uses therefore like SIFT corners)\n", - " # Pro: SIFT/SURF are scale and rotation invariant!\n", - " for images in npImages:\n", - " # Read image\n", - " image = cv2.imread(images[1], cv2.CV_LOAD_IMAGE_COLOR)\n", - " #cv2.cvtColor(image, gray, cv.CV_BGR2GRAY);\n", - " \n", - " # Method to detect keypoints (kp) and calculate the descripteurs (des) with one function call\n", - " # Each image has different amount of kp, but each kp has a describteur of fixed length (128)\n", - " kp, des = sift.detectAndCompute(image,None)\n", - " des_list.append(des)\n", - " \n", - " # Stack all the descriptors vertically in a numpy array\n", - " descriptors = des_list[0]\n", - " for descriptor in des_list[0:]:\n", - " descriptors = np.vstack((descriptors, descriptor)) \n", - " \n", - " #### Bag of Words Approach\n", - " ### 1. Step: using K-means cluster to create dictionary/vocabulary/codebook:\n", - " # Encoding is the quantization of the image kp/des that constitute the image to be classified. \n", - " # Basic encoding schemes work by first running K-means on the set of all des that you collect \n", - " # across multiple images.\n", - " # This builds what is known a dictionary/vocabulary/codebook represented by the centroids obtained from the clustering.\n", - " \n", - " # Perform k-means clustering -> creates the words from all describteurs -> this is the (dic) dictionary/vocabulary/codebook\n", - " # k: amount of different clusters to build! Will result in a feature length k\n", - " dic, variance = kmeans(descriptors, k, 1) \n", - " \n", - " ### 2. Step: encoding/coding/vector quantization(vq) to assign each descripteur the closest \"visual word\" from dictionary:\n", - " # At the end of this process, you end up with K representative \"visual words\" (the centroid of each cluster after \n", - " # K means ends) of your image descripteurs. These \"visual words\" represent what is usually understood as your \n", - " # visual dictionary. Once you have these visual words, encoding is the process of assigning \n", - " # each descripteur within your image the \"visual word\" (nearest neighbor) in the dictionary.\n", - " \n", - " npSurfHist = np.zeros((len(npImages), k), \"float32\")\n", - " for i in xrange(len(npImages)):\n", - " # vq: (Encoding) Assign words from the dictionary to each descripteur\n", - " words, distance = vq(des_list[i],dic)\n", - " \n", - " ### 3. Step: Pooling - calculate a histogram for each image\n", - " # Pooling refers to the process of representing an image as a \"bag of words\". \n", - " # The word bag here is meant to convey that once you have encoded each descripteur with a word (a number between 1 and K), \n", - " # you build a new representation (a bag) that discards the spatial relationship between the words that \n", - " # constitute your image.\n", - "\n", - " # This representation is often a histogram or a collection of spatially adjacent histograms of the desribteurs \n", - " # (i.e. histograms of values 1 to K) that together form your image. \"Pooling\" is thus the process of \n", - " # building a histogram of words (i.e. pooling ~ \"sampling\" words from the image to build a probability \n", - " # mass function of words)\n", - "\n", - " # To clarify, the purpose of pooling is two fold:\n", - " # By building a feature vector that is a histogram of words (as opposed to putting the full \"sentence of words\" \n", - " # in the feature vector), your descriptor will be invariant to changes in \"the ordering of words\". \n", - " # In computer vision this translates into invariance with respect to rotations and distortions of the image \n", - " # and object, which is a desirable thing to have.\n", - "\n", - " # If the dictionary is small compared to the length of the sentence, a histogram of words has less dimensions \n", - " # than the original vector. Less dimensions makes learning (training) much easier.\n", - " \n", - " \n", - " # Count the accuarance of each word (w) in image (i) to build histogram\n", - " for w in words:\n", - " npSurfHist[i][w] += 1\n", - " \n", - " #### 4. Step: Normalization of features vector (Can be changed to distribution like ColorHisto)\n", - " # Frequency divided by amount of words (k)\n", - " sumHist = sum(npSurfHist[i])\n", - " if(sumHist>0):\n", - " for x in range(0,k):\n", - " #npSurfHist[i][x] = npSurfHist[i][x]/k\n", - " npSurfHist[i][x] = npSurfHist[i][x]/sumHist #sumHist can be zero...change system\n", - " else: \n", - " print colored(\"WARNING NORMELIZATION: sumHIST is zero (0)\", 'red')\n", - " print colored(\"image: \" + images[1] + \"\\n\", 'red')\n", - " \n", - " \n", - " #stdSlr = StandardScaler().fit(npSurfHist)\n", - " #npSurfHist = stdSlr.transform(npSurfHist)\n", - " \n", - " return npSurfHist" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### SIFT Experimental - use SURF " - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "collapsed": false - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "(100L, 128L)\n" - ] - } - ], - "source": [ - "########### Feature SIFT (Scale-invariant feature transform cf. http://docs.opencv.org/master/da/df5/tutorial_py_sift_intro.html#gsc.tab=0)\n", - "# Api cf. http://docs.opencv.org/2.4/modules/nonfree/doc/feature_detection.html\n", - "import cv2\n", - "import numpy as np\n", - "\n", - "img = cv2.imread('D:/Caltech//airplanes//image_0306.jpg')\n", - "gray= cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)\n", - "\n", - "sift = cv2.SIFT(nfeatures=100)\n", - "#sift = cv2.xfeatures2d.SIFT_create()\n", - "\n", - "# Detector which detects the Keypoints in the Image\n", - "#kp = sift.detect(gray,None)\n", - "\n", - "# Just a visualization of the Keypoints in the Image\n", - "#img=cv2.drawKeypoints(gray,kp)\n", - "#cv2.imwrite('D:\\Sift-test\\sift_keypoints.jpg',img)\n", - "\n", - "# Another visualization with FLAG: draw a circle with size of keypoint and it will even show its orientation\n", - "#img=cv2.drawKeypoints(gray,kp,flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)\n", - "#cv2.imwrite('D:\\Sift-test\\sift_keypoints.jpg',img)\n", - "\n", - "# Method to compute the descripteurs after one has already detected the keypoints\n", - "#kp,des = sift.compute(gray,kp)\n", - "\n", - "#sift = cv2.xfeatures2d.SIFT_create()\n", - "#sift = cv2.SIFT()\n", - "\n", - "# Method to detect keypoints (kp) and calculate the descripteurs (des) with one function call\n", - "kp, des = sift.detectAndCompute(gray,None)\n", - "\n", - "print des.shape\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Functions to export calculated Data to csv " - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "#### Export Features to csv\n", - "def exportToCSV(pandasSorDF, filename):\n", - " #filename = datetime.datetime.now().strftime(\"%Y_%m_%d\") + \"-Feature\"\n", - " path = os.getcwdu() + \"\\\\\" + filename\n", - " \n", - " if os.path.isfile(path + \".csv\"):\n", - " for i in range(1,20):\n", - " testFileName = filename + \"-\" + str(i) + \".csv\"\n", - " if os.path.isfile(os.getcwdu() + \"\\\\\" + testFileName)!=True:\n", - " pandasSorDF.to_csv(testFileName)\n", - " break\n", - "\n", - " else:\n", - " pandasSorDF.to_csv(filename + \".csv\")\n" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def exportNumpyToCSV(numpyArray, filename):\n", - " #filename = datetime.datetime.now().strftime(\"%Y_%m_%d\") + \"-Feature\"\n", - " path = os.getcwdu() + \"\\\\\" + filename\n", - " \n", - " if os.path.isfile(path + \".csv\"):\n", - " for i in range(1,20):\n", - " testFileName = filename + \"-\" + str(i) + \".csv\"\n", - " if os.path.isfile(os.getcwdu() + \"\\\\\" + testFileName)!=True:\n", - " np.savetxt(testFileName, numpyArray, delimiter=\",\")\n", - " break\n", - "\n", - " else:\n", - " np.savetxt(filename + \".csv\", numpyArray, delimiter=\",\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Main Programm\n" - ] - }, - { - "cell_type": "code", - "execution_count": 40, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "# Imports\n", - "import os # for iteration throug directories\n", - "import pandas as pd # for Series and DataFrames\n", - "import cv2 # for OpenCV \n", - "import cv # for OpenCV\n", - "import datetime # for TimeStamp in CSVFile\n", - "from scipy.cluster.vq import * # for Clustering http://docs.scipy.org/doc/scipy/reference/cluster.vq.html\n", - "import numpy as np # for arrays\n", - "import time # for time calculations\n", - "from termcolor import colored #to color Warnings - PACKAGE MIGHT NOT BE AVAILABLE" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Extract images path from DataBase " - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": { - "collapsed": false - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "(9144L,)\n", - "Time to extract all images: 26.3950002193\n" - ] - } - ], - "source": [ - "start = time.time()\n", - "\n", - "# Determine the Database to extract features\n", - "path ='D:\\Caltech'\n", - "\n", - "# get dictionary to link classLabels Text to Integers\n", - "sClassLabels = getClassLabels(path)\n", - "\n", - "# Get all path from all images inclusive classLabel as Integer\n", - "dfImages = imgCrawl(path, sClassLabels)\n", - "print dfImages.classLabel.shape\n", - "\n", - "end = time.time()\n", - "print \"Time to extract all images: \" + str(end - start)" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "# CSV Output\n", - "fileNameClassLabels = datetime.datetime.now().strftime(\"%Y_%m_%d\") + \"-Caltech-ClassLabels\"\n", - "exportNumpyToCSV(dfImages.classLabel, fileNameClassLabels)\n", - "\n", - "fileNameClassLabels = datetime.datetime.now().strftime(\"%Y_%m_%d\") + \"-Caltech-ClassLabels-Description\"\n", - "exportToCSV(sClassLabels, fileNameClassLabels)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### RGB Color Histogram " - ] - }, - { - "cell_type": "code", - "execution_count": 47, - "metadata": { - "collapsed": false - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "(9144L, 48L)\n", - "Time to calculate RGBColorHistogram: 19.3360002041\n" - ] - } - ], - "source": [ - "start = time.time()\n", - "\n", - "# Calculate RGB Color Histogram wit 16 bins for each color -> feature length = 3 x 16 = 48\n", - "npRGBColorHistogram = calcRGBColorHisto(dfImages, 16)\n", - "print npRGBColorHistogram.shape\n", - "\n", - "end = time.time()\n", - "print \"Time to calculate RGBColorHistogram: \" + str(end - start)" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# CSV Output\n", - "fileNameColorHis = datetime.datetime.now().strftime(\"%Y_%m_%d\") + \"-Caltech-Feature-RGBColorHistogram\"\n", - "exportNumpyToCSV(npRGBColorHistogram, fileNameColorHis)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### HSV Color Histogram " - ] - }, - { - "cell_type": "code", - "execution_count": 45, - "metadata": { - "collapsed": false - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\u001b[31mWARNING NORMELIZATION: sumHIST is zero (0)\u001b[0m\n", - "\u001b[31mimage: D:\\Caltech\\BACKGROUND_Google\\image_0031.jpg\n", - "\u001b[0m\n", - "\u001b[31mWARNING NORMELIZATION: sumHIST is zero (0)\u001b[0m\n", - "\u001b[31mimage: D:\\Caltech\\menorah\\image_0022.jpg\n", - "\u001b[0m\n", - "(9144L, 14L)\n", - "Time to calculate HSVColorHistogram: 22.4220001698\n" - ] - } - ], - "source": [ - "start = time.time()\n", - "\n", - "# Calculate HSV Color Histogramm -> feature length = 8+3+3 = 14\n", - "\n", - "h_bins = 8 \n", - "s_bins = 3\n", - "v_bins = 3\n", - "histSize = [ h_bins, s_bins, v_bins ]\n", - "\n", - "npHSVColorHistogram = calcHSVColorHisto(dfImages, histSize)\n", - "print npHSVColorHistogram.shape\n", - "\n", - "end = time.time()\n", - "print \"Time to calculate HSVColorHistogram: \" + str(end - start)" - ] - }, - { - "cell_type": "code", - "execution_count": 46, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# CSV Output\n", - "fileNameColorHis = datetime.datetime.now().strftime(\"%Y_%m_%d\") + \"-Caltech-Feature-HSVColorHistogram\"\n", - "exportNumpyToCSV(npHSVColorHistogram, fileNameColorHis)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### SURF Histogram " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "start = time.time()\n", - "\n", - "# Calculate Surf Histogramm with K=100 Cluster\n", - "npSurfHistogram = calcSurfHisto(dfImages, 100)\n", - "print npSurfHistogram.shape\n", - "\n", - "end = time.time()\n", - "print \"Time to calculate SurfHistogram: \" + str(end - start)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# CSV Output\n", - "fileNameSurfHis = datetime.datetime.now().strftime(\"%Y_%m_%d\") + \"-Caltech-Feature-SurfHistogram\"\n", - "exportNumpyToCSV(npSurfHistogram, fileNameSurfHis)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 2", - "language": "python", - "name": "python2" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 2 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython2", - "version": "2.7.11" - } - }, - "nbformat": 4, - "nbformat_minor": 0 -} diff --git a/ipynb/FeatureExtraction-All_unix.ipynb b/ipynb/FeatureExtraction-All_unix.ipynb deleted file mode 100644 index fd972a80342a04b04fbd8a6b243fd211667c6e5e..0000000000000000000000000000000000000000 --- a/ipynb/FeatureExtraction-All_unix.ipynb +++ /dev/null @@ -1,531 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Code to Extract ColorHistograms for Database" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Author: Nikolas Hülsmann\n", - "#### Date: 2015-11-22" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Functions for Extract Data\n", - "\n", - "### Function to iterate through given directory and return images paths and classLabels" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "def imgCrawl(path, sClassLabels): #path to 'highest' folder\n", - " rootdir = path\n", - " df = pd.DataFrame()\n", - " \n", - " for subdir, dirs, files in os.walk(rootdir): # loop through subdirectories\n", - " for file in files:\n", - " pathOfFile = os.path.join(subdir, file) #path of file\n", - " head, classLabel = os.path.split(os.path.split(pathOfFile)[0]) # get directoryname of file as classLabel\n", - " \n", - " # assign integer label for dataframe\n", - " classLabel = sClassLabels[sClassLabels == classLabel].index[0]\n", - " df = df.append({'classLabel': classLabel, 'pathOfFile': pathOfFile}, ignore_index=True) \n", - " \n", - " return df" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Function to determine Class-Labels with Integer representation" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# function to determine Class-labels and return Series\n", - "def getClassLabels(path):\n", - " data = os.listdir(path) # listdir returns all subdirectories\n", - " index = range(0,len(data))\n", - " \n", - " return pd.Series(data,index)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Function to calculate the ColorHistogram for given Images " - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "#### Calculate ColorHistograms for all images\n", - "\n", - "### Points to improve: \n", - "# - use HSV color spectrum\n", - "# - change function: parameter how many bins of ColorHistogramm (feature length)\n", - "\n", - "\n", - "# dfImages: Dataframe with paths to all images - use function imgCrawl\n", - "# numberOfBins_: Number of bins Histogram\n", - "def calcColorHisto(dfImages_, numberOfBins_):\n", - " # Initialize function\n", - " df = pd.DataFrame()\n", - " npImages = dfImages_.values\n", - " numberOfBins = numberOfBins_\n", - " npColorHist = np.zeros((len(npImages), numberOfBins*3), \"float32\")\n", - " i=0\n", - " \n", - " ## algo\n", - " for images in npImages:\n", - " image = cv2.imread(images[1])\n", - " \n", - " # Image Size for Normalization\n", - " height, width, channels = image.shape\n", - " img_size = height * width\n", - " \n", - " # Split into color chanels rgb\n", - " chans = cv2.split(image)\n", - " colors = (\"b\", \"g\", \"r\")\n", - " \n", - " histogram = []\n", - "\n", - " ########### Feature Color Histogram (cf. http://docs.opencv.org/2.4/doc/tutorials/imgproc/histograms/histogram_calculation/histogram_calculation.html) # loop over the image channels\n", - " for (chan, color) in zip(chans, colors): \n", - " \n", - " # Calculate Color Histogram - 16 bins cf. paper (test with 64 has shown that die result is similair in score)\n", - " # Seperates the intesity for each color from 0 to 256, and creates 16 bins of same size: 0-15, 16-31, .. , 241-256\n", - " hist = cv2.calcHist([chan], [0], None, [numberOfBins], [0, 256])\n", - "\n", - " # to get raw values\n", - " hist = hist[:,0]\n", - " \n", - " # Normalize to a Distrubution from 0 to 1 throug calculating for each color channel (red/blue/green): \n", - " # (number of pixels in bin)/(pixel size of image)\n", - " #hist[:] = [x / img_size for x in hist]\n", - " hist[:] = [x / sum(hist) for x in hist]\n", - " \n", - "\n", - " # Normalize with MinMax from 0 to 1 -> feature scaling\n", - " #cv2.normalize(hist, hist, 0, 1, cv2.NORM_MINMAX)\n", - " \n", - " histogram.extend(hist)\n", - "\n", - " # append features_colHist to df\n", - " npColorHist[i] = histogram\n", - " i = i+1\n", - " #df = df.append({'ColHisto': features_colHist}, ignore_index=True) \n", - " \n", - " return npColorHist" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Function to calculate Surf Histogram" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "################# FEATURE SURF (cf. http://docs.opencv.org/3.0-beta/doc/py_tutorials/py_feature2d/py_surf_intro/py_surf_intro.html#surf)\n", - "# API cf. http://docs.opencv.org/2.4/modules/nonfree/doc/feature_detection.html\n", - "\n", - "#### Calculate Histogramm of SURF Descripteurs with Bag Of Words appraoch for all images\n", - "\n", - "### Points to improve: \n", - "# - use spatial histogram: http://www.di.ens.fr/willow/events/cvml2011/materials/practical-classification/\n", - "# - change function: parameter how many K clustes/feature length (in regard of overfitting)\n", - "\n", - "\n", - "# path to highest folder\n", - "# dfImages: Dataframe with paths to all images - use function imgCrawl\n", - "# k: number of K-Cluster -> length of feature vector\n", - "def calcSurfHisto(dfImages_, k_):\n", - " \n", - " # Initialize function\n", - " df = pd.DataFrame()\n", - " npImages = dfImages_.values\n", - " k = k_\n", - " \n", - " # List where all the descriptors are stored\n", - " des_list = []\n", - " \n", - " #### Feature Detection and Description (Surf): \n", - " # Detect (localize) for each image the keypoints (Points of Interest in an image - Surf uses therefore like SIFT corners)\n", - " # Pro: SIFT/SURF are scale and rotation invariant!\n", - " for images in npImages:\n", - " # Read image\n", - " image = cv2.imread(images[1])\n", - " \n", - " # Method to detect keypoints (kp) and calculate the descripteurs (des) with one function call\n", - " # Each image has different amount of kp, but each kp has a describteur of fixed length (128)\n", - " kp, des = sift.detectAndCompute(image,None)\n", - " des_list.append(des)\n", - " \n", - " # Stack all the descriptors vertically in a numpy array\n", - " descriptors = des_list[0][1]\n", - " \n", - " for descriptor in des_list[0:]:\n", - " \n", - " descriptors = np.vstack((descriptors, descriptor)) \n", - " \n", - " #### Bag of Words Approach\n", - " ### 1. Step: using K-means cluster to create dictionary/vocabulary/codebook:\n", - " # Encoding is the quantization of the image kp/des that constitute the image to be classified. \n", - " # Basic encoding schemes work by first running K-means on the set of all des that you collect \n", - " # across multiple images.\n", - " # This builds what is known a dictionary/vocabulary/codebook represented by the centroids obtained from the clustering.\n", - " \n", - " # Perform k-means clustering -> creates the words from all describteurs -> this is the (dic) dictionary/vocabulary/codebook\n", - " # k: amount of different clusters to build! Will result in a feature length k\n", - " dic, variance = kmeans(descriptors, k, 1) \n", - " \n", - " ### 2. Step: encoding/coding/vector quantization(vq) to assign each descriptor the closest \"visual word\" from dictionary:\n", - " # At the end of this process, you end up with K representative \"visual words\" (the centroid of each cluster after \n", - " # K means ends) of your image descripteurs. These \"visual words\" represent what is usually understood as your \n", - " # visual dictionary. Once you have these visual words, encoding is the process of assigning \n", - " # each descriptor within your image the \"visual word\" (nearest neighbor) in the dictionary.\n", - " \n", - " npSurfHist = np.zeros((len(npImages), k), \"float32\")\n", - " for i in xrange(len(npImages)):\n", - " # vq: (Encoding) Assign words from the dictionary to each descripteur\n", - " words, distance = vq(des_list[i],dic)\n", - " \n", - " ### 3. Step: Pooling - calculate a histogram for each image\n", - " # Pooling refers to the process of representing an image as a \"bag of words\". \n", - " # The word bag here is meant to convey that once you have encoded each descripteur with a word (a number between 1 and K), \n", - " # you build a new representation (a bag) that discards the spatial relationship between the words that \n", - " # constitute your image.\n", - "\n", - " # This representation is often a histogram or a collection of spatially adjacent histograms of the desribteurs \n", - " # (i.e. histograms of values 1 to K) that together form your image. \"Pooling\" is thus the process of \n", - " # building a histogram of words (i.e. pooling ~ \"sampling\" words from the image to build a probability \n", - " # mass function of words)\n", - "\n", - " # To clarify, the purpose of pooling is two fold:\n", - " # By building a feature vector that is a histogram of words (as opposed to putting the full \"sentence of words\" \n", - " # in the feature vector), your descriptor will be invariant to changes in \"the ordering of words\". \n", - " # In computer vision this translates into invariance with respect to rotations and distortions of the image \n", - " # and object, which is a desirable thing to have.\n", - "\n", - " # If the dictionary is small compared to the length of the sentence, a histogram of words has less dimensions \n", - " # than the original vector. Less dimensions makes learning (training) much easier.\n", - " \n", - " \n", - " # Count the accuarance of each word (w) in image (i) to build histogram\n", - " for w in words:\n", - " npSurfHist[i][w] += 1\n", - " \n", - " #### 4. Step: Normalization of features vector (Can be changed to distribution like ColorHisto)\n", - " # Frequency divided by amount of words (k)\n", - " summe = sum(npSurfHist[i])\n", - " for x in range(0,k):\n", - " #npSurfHist[i][x] = npSurfHist[i][x]/k\n", - " npSurfHist[i][x] = npSurfHist[i][x]/summe\n", - " \n", - " #stdSlr = StandardScaler().fit(npSurfHist)\n", - " #npSurfHist = stdSlr.transform(npSurfHist)\n", - " \n", - " return npSurfHist" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### SIFT Experimental - use SURF " - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": { - "collapsed": false - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "(100, 128)\n" - ] - } - ], - "source": [ - "########### Feature SIFT (Scale-invariant feature transform cf. http://docs.opencv.org/master/da/df5/tutorial_py_sift_intro.html#gsc.tab=0)\n", - "# Api cf. http://docs.opencv.org/2.4/modules/nonfree/doc/feature_detection.html\n", - "import cv2\n", - "import numpy as np\n", - "\n", - "img = cv2.imread('../../03-jeux-de-donnees/101_ObjectCategories/airplanes/image_0306.jpg')\n", - "gray= cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)\n", - "\n", - "sift = cv2.SIFT(nfeatures=100)\n", - "#sift = cv2.xfeatures2d.SIFT_create()\n", - "\n", - "# Detector which detects the Keypoints in the Image\n", - "#kp = sift.detect(gray,None)\n", - "\n", - "# Just a visualization of the Keypoints in the Image\n", - "#img=cv2.drawKeypoints(gray,kp)\n", - "#cv2.imwrite('D:\\Sift-test\\sift_keypoints.jpg',img)\n", - "\n", - "# Another visualization with FLAG: draw a circle with size of keypoint and it will even show its orientation\n", - "#img=cv2.drawKeypoints(gray,kp,flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)\n", - "#cv2.imwrite('D:\\Sift-test\\sift_keypoints.jpg',img)\n", - "\n", - "# Method to compute the descripteurs after one has already detected the keypoints\n", - "#kp,des = sift.compute(gray,kp)\n", - "\n", - "#sift = cv2.xfeatures2d.SIFT_create()\n", - "#sift = cv2.SIFT()\n", - "\n", - "# Method to detect keypoints (kp) and calculate the descripteurs (des) with one function call\n", - "kp, des = sift.detectAndCompute(gray,None)\n", - "\n", - "print (des.shape)\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Functions to export calculated Data to csv " - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "#### Export Features to csv\n", - "def exportToCSV(pandasSorDF, filename):\n", - " #filename = datetime.datetime.now().strftime(\"%Y_%m_%d\") + \"-Feature\"\n", - " path = os.getcwdu() + \"\\\\\" + filename\n", - " \n", - " if os.path.isfile(path + \".csv\"):\n", - " for i in range(1,20):\n", - " testFileName = filename + \"-\" + str(i) + \".csv\"\n", - " if os.path.isfile(os.getcwdu() + \"\\\\\" + testFileName)!=True:\n", - " pandasSorDF.to_csv(testFileName)\n", - " break\n", - "\n", - " else:\n", - " pandasSorDF.to_csv(filename + \".csv\")\n" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def exportNumpyToCSV(numpyArray, filename):\n", - " #filename = datetime.datetime.now().strftime(\"%Y_%m_%d\") + \"-Feature\"\n", - " path = os.getcwdu() + \"\\\\\" + filename\n", - " \n", - " if os.path.isfile(path + \".csv\"):\n", - " for i in range(1,20):\n", - " testFileName = filename + \"-\" + str(i) + \".csv\"\n", - " if os.path.isfile(os.getcwdu() + \"\\\\\" + testFileName)!=True:\n", - " np.savetxt(testFileName, numpyArray, delimiter=\",\")\n", - " break\n", - "\n", - " else:\n", - " np.savetxt(filename + \".csv\", numpyArray, delimiter=\",\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Main Programm\n" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Imports\n", - "import os # for iteration throug directories\n", - "import pandas as pd # for Series and DataFrames\n", - "import cv2 # for OpenCV \n", - "import datetime # for TimeStamp in CSVFile\n", - "from scipy.cluster.vq import * # for Clustering http://docs.scipy.org/doc/scipy/reference/cluster.vq.html\n", - "import numpy as np # for arrays\n", - "import time # for time calculations" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": { - "collapsed": false - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "(9145,)\n", - "Time to extract all images: 28.9075498581\n" - ] - } - ], - "source": [ - "start = time.time()\n", - "\n", - "# Determine the Database to extract features\n", - "path ='../../03-jeux-de-donnees/101_ObjectCategories'\n", - "\n", - "# get dictionary to link classLabels Text to Integers\n", - "sClassLabels = getClassLabels(path)\n", - "\n", - "# Get all path from all images inclusive classLabel as Integer\n", - "dfImages = imgCrawl(path, sClassLabels)\n", - "\n", - "print dfImages.classLabel.shape\n", - "\n", - "fileNameClassLabels = datetime.datetime.now().strftime(\"%Y_%m_%d\") + \"-Caltech-ClassLabels\"\n", - "exportNumpyToCSV(dfImages.classLabel, fileNameClassLabels)\n", - "\n", - "fileNameClassLabels = datetime.datetime.now().strftime(\"%Y_%m_%d\") + \"-Caltech-ClassLabels-Description\"\n", - "#exportToCSV(sClassLabels, fileNameClassLabels)\n", - "\n", - "end = time.time()\n", - "print \"Time to extract all images: \" + str(end - start)" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": { - "collapsed": false - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "(9145, 48)\n", - "Time to calculate ColorHistogram: 41.924052\n" - ] - } - ], - "source": [ - "start = time.time()\n", - "\n", - "# Calculate Color Histogramm wit 16 bins for each color -> feature length = 3 x 16 = 48\n", - "npColorHistogram = calcColorHisto(dfImages, 16)\n", - "\n", - "print npColorHistogram.shape\n", - "\n", - "fileNameColorHis = datetime.datetime.now().strftime(\"%Y_%m_%d\") + \"-Caltech-Feature-ColorHistogram\"\n", - "#exportNumpyToCSV(npColorHistogram, fileNameColorHis)\n", - "\n", - "end = time.time()\n", - "print \"Time to calculate ColorHistogram: \" + str(end - start)" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": { - "collapsed": false - }, - "outputs": [ - { - "ename": "ValueError", - "evalue": "all the input array dimensions except for the concatenation axis must match exactly", - "traceback": [ - "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[1;31mValueError\u001b[0m Traceback (most recent call last)", - "\u001b[1;32m<ipython-input-20-43b9b016b2b9>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m()\u001b[0m\n\u001b[0;32m 2\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 3\u001b[0m \u001b[1;31m# Calculate Surf Histogramm with K=100 Cluster\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 4\u001b[1;33m \u001b[0mnpSurfHistogram\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mcalcSurfHisto\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mdfImages\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;36m100\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 5\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 6\u001b[0m \u001b[1;32mprint\u001b[0m \u001b[0mnpSurfHistogram\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mshape\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", - "\u001b[1;32m<ipython-input-13-c8d37ffc0446>\u001b[0m in \u001b[0;36mcalcSurfHisto\u001b[1;34m(dfImages_, k_)\u001b[0m\n\u001b[0;32m 39\u001b[0m \u001b[1;32mfor\u001b[0m \u001b[0mdescriptor\u001b[0m \u001b[1;32min\u001b[0m \u001b[0mdes_list\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 40\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 41\u001b[1;33m \u001b[0mdescriptors\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mnp\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mvstack\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mdescriptors\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mdescriptor\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 42\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 43\u001b[0m \u001b[1;31m#### Bag of Words Approach\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", - "\u001b[1;32m/home/doob/anaconda2/lib/python2.7/site-packages/numpy/core/shape_base.pyc\u001b[0m in \u001b[0;36mvstack\u001b[1;34m(tup)\u001b[0m\n\u001b[0;32m 228\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 229\u001b[0m \"\"\"\n\u001b[1;32m--> 230\u001b[1;33m \u001b[1;32mreturn\u001b[0m \u001b[0m_nx\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mconcatenate\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m[\u001b[0m\u001b[0matleast_2d\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0m_m\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;32mfor\u001b[0m \u001b[0m_m\u001b[0m \u001b[1;32min\u001b[0m \u001b[0mtup\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;36m0\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 231\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 232\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0mhstack\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mtup\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", - "\u001b[1;31mValueError\u001b[0m: all the input array dimensions except for the concatenation axis must match exactly" - ], - "output_type": "error" - } - ], - "source": [ - "start = time.time()\n", - "\n", - "# Calculate Surf Histogramm with K=100 Cluster\n", - "npSurfHistogram = calcSurfHisto(dfImages, 100)\n", - "\n", - "print npSurfHistogram.shape\n", - "\n", - "fileNameSurfHis = datetime.datetime.now().strftime(\"%Y_%m_%d\") + \"-Caltech-Feature-SurfHistogram\"\n", - "#exportNumpyToCSV(npSurfHistogram, fileNameSurfHis)\n", - "\n", - "end = time.time()\n", - "print \"Time to calculate SurfHistogram: \" + str(end - start)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 2", - "language": "python", - "name": "python2" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 2 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython2", - "version": "2.7.10" - } - }, - "nbformat": 4, - "nbformat_minor": 0 -} diff --git a/ipynb/HOG Extraction.ipynb b/ipynb/HOG Extraction.ipynb deleted file mode 100644 index baafb339c6d77094303a9b186d45f1009c3501f0..0000000000000000000000000000000000000000 --- a/ipynb/HOG Extraction.ipynb +++ /dev/null @@ -1,354 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "<h1>Imports </h1>" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "import os as os # for iteration throug directories\n", - "import pandas as pd # for Series and DataFrames\n", - "import cv2 # for OpenCV \n", - "import numpy as np # for arrays\n", - "import time # for time calculations\n", - "from feature_extraction_try import imgCrawl, getClassLabels #for fetching images\n", - "from skimage.feature import hog #calculates HOGs \n", - "from sklearn.cluster import MiniBatchKMeans #for clustering" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "<h1>HOG computation :</h1>\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In this file, we aim to compute a HOG descriptor thas has the same dimension for each image. So we used a bag of word approach : \n", - "<ul>\n", - " <li>Resizing images to a 0mod5 height and width</li>\n", - " <li>Sequencing each image in 5*5 cells</li>\n", - " <li>Computing local histograms on each cell</li>\n", - " <li>Clustering the local histograms</li>\n", - " <li>Binning the local histograms to create our feature</li>\n", - "</ul>" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "<h2>Resizing images : </h2>" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We decided to resize images by duplicating the missing pixels on each side (width and height) because it seem to be the less disturbing way for gradient computation. \n", - "Another possible way is to crop each image, but it may delete useful information. \n", - "Returns the resized image as a np.array " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def reSize(image, CELL_DIMENSION):\n", - " height, width, channels = image.shape\n", - " \n", - " if height%CELL_DIMENSION==0 and width%CELL_DIMENSION==0:\n", - " resizedImage = image\n", - " \n", - " elif width%CELL_DIMENSION==0:\n", - " missingPixels = CELL_DIMENSION-height%CELL_DIMENSION\n", - " resizedImage = cv2.copyMakeBorder(image,0,missingPixels,0,\\\n", - " 0,cv2.BORDER_REPLICATE)\n", - " \n", - " elif height%CELL_DIMENSION==0:\n", - " missingPixels = CELL_DIMENSION-width%CELL_DIMENSION\n", - " resizedImage = cv2.copyMakeBorder(image,0,0,0,missingPixels,\\\n", - " cv2.BORDER_REPLICATE)\n", - " \n", - " else:\n", - " missingWidthPixels = CELL_DIMENSION-width%CELL_DIMENSION\n", - " missingHeightPixels = CELL_DIMENSION-height%CELL_DIMENSION\n", - " resizedImage = cv2.copyMakeBorder(image,0,missingHeightPixels,0,\\\n", - " missingWidthPixels,cv2.BORDER_REPLICATE)\n", - " return resizedImage" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "<h2>Sequencing images : </h2>" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here, we just tranfrom our corpus in $(5*5)$ cells to be able to execute the bag of words approach. \n", - "\n", - "Returns a (nbImages$*$imageHeight mod 5$*$imageWidth mod 5$*$nbChannels) np.array" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def imageSequencing(npImages, CELL_DIMENSION):\n", - " cells=[]\n", - " \n", - " for k in range(len(npImages)):\n", - " image = cv2.imread(npImages[k][1])\n", - " resizedImage = reSize(image, CELL_DIMENSION)\n", - " height, width, channels = resizedImage.shape\n", - " cells.append(\\\n", - " np.array([\\\n", - " resizedImage[\\\n", - " j*CELL_DIMENSION:j*CELL_DIMENSION+CELL_DIMENSION,\\\n", - " i*CELL_DIMENSION:i*CELL_DIMENSION+CELL_DIMENSION] \\\n", - " for i in range(width/CELL_DIMENSION) \\\n", - " for j in range(height/CELL_DIMENSION)\\\n", - " ])\\\n", - " )\n", - " return np.array(cells) " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "<h2>Compute HOG descriptor on each cell : </h2>" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here we convert our images in grayscale toi be able to apply our localHistogram function (doc : http://scikit-image.org/docs/dev/auto_examples/plot_hog.html)\n", - "\n", - "Then we compute local HOGs on our cells with NB_ORIENTATIONS orientations" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def computeLocalHistograms(cells, NB_ORIENTATIONS, CELL_DIMENSION):\n", - " localHistograms = np.array([\\\n", - " np.array([\\\n", - " hog(cv2.cvtColor( cell, \\\n", - " cv2.COLOR_BGR2GRAY), \\\n", - " orientations=NB_ORIENTATIONS, \\\n", - " pixels_per_cell=(CELL_DIMENSION,\\\n", - " CELL_DIMENSION),\\\n", - " cells_per_block=(1,1)) \\\n", - " for cell in image]) \\\n", - " for image in cells])\n", - " return localHistograms" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "<h2>Clustering local HOGs : </h2>" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here we search for NB_CLUSTERS clusters in all our localHistograms to be able to bin later our localHistograms.\n", - "\n", - "doc for MiniBatchKMeans : http://scikit-learn.org/stable/modules/generated/sklearn.cluster.MiniBatchKMeans.html" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def clusterGradients(localHistograms, NB_CLUSTERS, MAXITER):\n", - " sizes = np.array([len(localHistogram) for localHistogram in localHistograms])\n", - " nbImages = len(localHistograms)\n", - " flattenedHogs = np.array([cell for image in localHistograms for cell in image])\n", - " miniBatchKMeans = MiniBatchKMeans(n_clusters=NB_CLUSTERS, max_iter=MAXITER, \\\n", - " compute_labels=True)\n", - " localHistogramLabels = miniBatchKMeans.fit_predict(flattenedHogs)\n", - " return localHistogramLabels, sizes" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "<h2>Binning local HOGs : </h2>" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Then we just need to bin our local HOGs to avec a constant-sized feature based on HOG to describe our images" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def makeHistograms(labels, NB_CLUSTERS, sizes):\n", - " indiceInLabels = 0\n", - " hogs = []\n", - " for image in sizes:\n", - " histogram = np.zeros(NB_CLUSTERS)\n", - " for i in range(image):\n", - " histogram[labels[indiceInLabels+i]] += 1\n", - " hogs.append(histogram)\n", - " indiceInLabels+=i \n", - " return np.array(hogs)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "<h2>Global function</h2>" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def extractHOGFeature(npImages, CELL_DIMENSION, NB_ORIENTATIONS, \\\n", - " NB_CLUSTERS, MAXITER):\n", - " cells = imageSequencing(npImages, CELL_DIMENSION)\n", - " localHistograms = computeLocalHistograms(cells)\n", - " localHistogramLabels, sizes = clusterGradients(localHistograms, \\\n", - " NB_CLUSTERS, MAXITER)\n", - " hogs = makeHistograms(localHistogramLabels, NB_CLUSTERS, sizes)\n", - " return hogs" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "<h1>Test zone</h1>" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "if __name__ == '__main__':\n", - "\n", - "\n", - " start = time.time()\n", - " path ='/home/doob/Dropbox/Marseille/OMIS-Projet/03-jeux-de-donnees/101_ObjectCategories'\n", - " testNpImages = [ [1,'testImage.jpg'] ]\n", - " CELL_DIMENSION = 5\n", - " NB_ORIENTATIONS = 8\n", - " NB_CLUSTERS = 12\n", - " MAXITER = 100\n", - "\n", - " print \"Fetching Images in \" + path\n", - " # get dictionary to link classLabels Text to Integers\n", - " sClassLabels = getClassLabels(path)\n", - " # Get all path from all images inclusive classLabel as Integer\n", - " dfImages = imgCrawl(path, sClassLabels)\n", - " npImages = dfImages.values\n", - " extractedTime = time.time()\n", - " print \"Extracted images in \" + str(extractedTime-start) +'sec'\n", - " print \"Sequencing Images ...\"\n", - " blocks = imageSequencing(testNpImages, 5)\n", - " sequencedTime = time.time()\n", - " print \"Sequenced images in \" + str(sequencedTime-extractedTime) +'sec'\n", - " print \"Computing gradient on each block ...\"\n", - " gradients = computeLocalHistograms(blocks, NB_ORIENTATIONS, CELL_DIMENSION)\n", - " hogedTime = time.time()\n", - " print \"Computed gradients in \" + str(hogedTime - sequencedTime) + 'sec'\n", - " print \"Clustering gradients ...\"\n", - " gradientLabels, sizes = clusterGradients(gradients, NB_CLUSTERS, MAXITER)\n", - " clusteredItme = time.time()\n", - " print \"Clustered gradients in \" + str(hogedTime - sequencedTime) + 'sec'\n", - " print \"Computing histograms ...\"\n", - " histograms = makeHistograms(gradientLabels, NB_CLUSTERS, sizes)\n", - " end = time.time()\n", - " print \"Computed histograms in \" + str(int(end - hogedTime)) + 'sec'\n", - " print \"Histogram shape : \" +str(histograms.shape)\n", - " print \"Total time : \" + str(end-start) + 'sec'\n", - " #hogs = extractHOGFeature(testNpImages, CELL_DIMENSION, \\\n", - " # NB_ORIENTATIONS, NB_CLUSTERS, MAXITER)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 2", - "language": "python", - "name": "python2" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 2 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython2", - "version": "2.7.11" - } - }, - "nbformat": 4, - "nbformat_minor": 0 -} diff --git a/ipynb/How are the folds and splits generated.ipynb b/ipynb/How are the folds and splits generated.ipynb deleted file mode 100644 index f86fe94ba9515d23756561a363a55e3094daa50b..0000000000000000000000000000000000000000 --- a/ipynb/How are the folds and splits generated.ipynb +++ /dev/null @@ -1,138 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Randomized example selection for classification\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Train/test split generation " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The train/test splits are generated in the `execution.genSplits` function. It's task is to generate an train/test split for each statistical itetarion. In order to do that, it is fed by the following inputs \n", - "* `labels` are the data labels for all the dataset, \n", - "* `splitRatio` is a real number giving the ratio |test|/|all|,\n", - "* `statsIterRandomStates` is a list of `numpy` random states used to generate reproductible pseudo-random numbers\n", - "\n", - "The main operation in this function is done by the `sklearn.model_selection.StratifiedShuffleSplit` function which returns folds that are made by preserving the percentage of samples for each class.\n", - "In this case we askittosplit the dataset in two subsets with the asked test size. It then returns a shuffled train/test split while preserving the percentage of samples for each class.\n", - "We store the examples indices in two `np.array`s called `trainIndices` and `testIndices`\n", - "All the algortihms will then train (hyper-parameters cross-validation & learning) on the trainIndices. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def genSplits(labels, splitRatio, statsIterRandomStates):\n", - " \"\"\"Used to gen the train/test splits using one or multiple random states\n", - " classificationIndices is a list of train/test splits\"\"\"\n", - " indices = np.arange(len(labels))\n", - " splits = []\n", - " for randomState in statsIterRandomStates:\n", - " foldsObj = sklearn.model_selection.StratifiedShuffleSplit(n_splits=1,\n", - " random_state=randomState,\n", - " test_size=splitRatio)\n", - " folds = foldsObj.split(indices, labels)\n", - " for fold in folds:\n", - " train_fold, test_fold = fold\n", - " trainIndices = indices[train_fold]\n", - " testIndices = indices[test_fold]\n", - " splits.append([trainIndices, testIndices])\n", - "\n", - " return splits" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Multiclass problems" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To be able to use the platform on multiclass problems, a one-versus-one method is implemented. \n", - "In orderto use one-versus-one we need to modify the train/test splits generated by the previouslydescribed founction. \n", - "If the problem the platformis asked to resolve is multiclass then, it will generate all the possible two-class combinations to divide the main problem in multiple biclass ones. \n", - "In order to adapt each split, the `genMulticlassLabels` function will create new train/test splits by : \n", - "* Generating an `oldIndices` list containing all the examples indices that have their label in the combination\n", - "* Generate a new train split by selecting only the indices of `trainIndices` that have their labels in the combination.\n", - "* Do the samething for the test indices\n", - "* Copy the old `testIndices` variable in a new one called `testIndicesMulticlass` that will be used to predict on the entire dataset once the algorithm has learn to distinguish two classes\n", - "* Generate a new `label` array by replacing all the labels that are not in the combination by -100 to flag them as unseen labels during the training phase. \n", - "\n", - "Then the function will return a triplet : \n", - "* `multiclassLabels` is a list containing, for each combination, the newly generated labels with ones and zeros for each of the labels in the combination and -100 for the others.\n", - "* `labelsIndices` is a list contaningall the combinations,\n", - "* `indicesMulticlass` is a list containig triplets for each statistical iteration :\n", - " * `trainIndices` are the indices used for training that were picked only in the two classes of the combination (at the second step of the previous list),\n", - " * `testIndices` are the indices used for testing the biclass-generalization capacity of each biclass classifier learned on `trainIndices` that were picked only in the two classes of the combination (at the third step of the previous list),\n", - " * `tesIndicesMulticlass` are the indices described at the fourth setp of the previous list. " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Cross-validation folds " - ] - }, - { - "cell_type": "markdown", - "metadata": { - "collapsed": true - }, - "source": [ - "The cross validation folds are generated using a `StratifiedKFold` object, for the `sklearn.model_selection` library. \n", - "* For all the **monoview** algorithms, these objects (one for each statistical iteration) are then fed in a `sklearn.model_selection` `RandomisedSearchCV` object. So we don't have any custom stuff about cross-vaildation folds in the monoview case\n", - "* In the **multiview** case, they are used in the `utils.HyperParametersSearch` module, in the `randomizedSearch` function. In this case, they are used to split the learning set with `multiviewFolds = KFolds.split(learningIndices, labels[learningIndices])` and then used in `for trainIndices, testIndices in multiviewFolds:`" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 2", - "language": "python", - "name": "python2" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 2 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython2", - "version": "2.7.13" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/ipynb/How to add a monoview classifier.ipynb b/ipynb/How to add a monoview classifier.ipynb deleted file mode 100644 index ab2ebf32638f312ffaab649247a722bd3336ddf2..0000000000000000000000000000000000000000 --- a/ipynb/How to add a monoview classifier.ipynb +++ /dev/null @@ -1,100 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Monoview classifier framework" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## File addition" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "* In the `MonoviewClassifiers` package, you need to add a python module called after your monoview classifier (let's call it MOC for **MO**noview **C**lassifier)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## The `MOC.py` file" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In this file, you need to add several functions forthe platform to be able to use your classifier, they are alllisted below : " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### `canProbas`" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This function is just used to knowif the classifier can predict a probability for each label instead of just predicting the a label." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def canProbas():\n", - " return True" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### `fit`" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This function returns a fitted sklearn classifier object" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 2", - "language": "python", - "name": "python2" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 2 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython2", - "version": "2.7.13" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/test_database.hdf5 b/test_database.hdf5 deleted file mode 100644 index 03b4f97920daf6730f227e00c490545b339cd8ef..0000000000000000000000000000000000000000 Binary files a/test_database.hdf5 and /dev/null differ