├── .gitignore ├── Clarifai_Logo.png ├── LICENSE ├── README.md ├── custom_train.js ├── index.html ├── keys.js ├── package.json └── predict.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | keys.js 3 | -------------------------------------------------------------------------------- /Clarifai_Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clarifai/javascript-starter/HEAD/Clarifai_Logo.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2016 Clarifai, Inc. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # javascript-starter 2 | A few simple examples to help you get started using the Clarifai Javascript client and API 3 | 4 | ## How to get started 5 | Download this repo, simply invoke 6 | ```script 7 | $ npm install 8 | ``` 9 | 10 | ## Usage 11 | 12 | To get started, create an account at [developer.clarifai.com](http://developer.clarifai.com). 13 | 14 | Create an application, and get your API Key. 15 | 16 | This basic starter uses your API Key to make prediction calls. This will never expire so you only have to fill it in once. 17 | 18 | You'll notice that in the `.gitignore` file, it references a `keys.js` file. This is for security purposes, so you don't share your API Key with others. Add the following to that file: 19 | 20 | ``` 21 | var myApiKey = 'YOUR API KEY HERE'; 22 | ``` 23 | 24 | You'll also notice a custom_train.js file which serves as a reference for Custom Training. Any custom models that you create (under these credentials) will appear in the dropdown menu on index.html, next to the "Custom" label 25 | 26 | ## Example Output 27 | 28 | 29 | 30 | Note the "Add image to Application" button on the bottom left of the image. This will automatically add the image to the application that is associated with your key! 31 | -------------------------------------------------------------------------------- /custom_train.js: -------------------------------------------------------------------------------- 1 | // Require the client 2 | var Clarifai = require('clarifai'); 3 | var fs = require('fs'); 4 | var FileReader = require('filereader'); 5 | 6 | // instantiate a new Clarifai app passing in your clientId and clientSecret 7 | var app = new Clarifai.App( 8 | CLIENT_ID, 9 | CLIENT_SECRET 10 | ); 11 | 12 | trainModel(); 13 | 14 | // once inputs are created, create model by giving name and list of concepts 15 | function createModel() { 16 | app.models.create('pets', ["dog", "cat"]).then( 17 | (response) => { 18 | console.log(response); 19 | }, 20 | (error) => { 21 | console.error(error); 22 | } 23 | ); 24 | } 25 | 26 | // inputs with concept using urls 27 | function addInputsWithUrls(urls, isPostive, concept){ 28 | for (index in urls){ 29 | app.inputs.create({ 30 | url: urls[index], 31 | concepts: [ 32 | { 33 | id: concept, 34 | value: isPostive 35 | } 36 | ] 37 | }).then( 38 | (response) => { 39 | console.log(response); 40 | }, 41 | (error) => { 42 | console.error(error); 43 | } 44 | ); 45 | } 46 | } 47 | 48 | 49 | // inputs with concept using filepath 50 | function addInputsWithFiles(filepath, isPostive, concept){ 51 | 52 | for(data of getBase64s(getFiles(filepath))){ 53 | app.inputs.create({ 54 | base64: data, 55 | concepts: [ 56 | { 57 | id: concept, 58 | value: isPostive 59 | } 60 | ] 61 | }); 62 | } 63 | } 64 | 65 | 66 | function getFiles(dir){ 67 | fileList = []; 68 | 69 | var files = fs.readdirSync(dir); 70 | for(var i in files){ 71 | if (!files.hasOwnProperty(i) || i == 0) continue; 72 | var name = dir+'/'+files[i]; 73 | if (!fs.statSync(name).isDirectory()){ 74 | fileList.push(name); 75 | } 76 | } 77 | return fileList; 78 | } 79 | 80 | function getBase64s(fileList) { 81 | var FR = new FileReader(); 82 | base64s = []; 83 | for(index in fileList) { 84 | base64s.push(base64_encode(fileList[index])); 85 | } 86 | return base64s; 87 | } 88 | 89 | // function to encode file data to base64 encoded string 90 | function base64_encode(file) { 91 | // read binary data 92 | var bitmap = fs.readFileSync(file); 93 | // convert binary data to base64 encoded string 94 | return new Buffer(bitmap).toString('base64'); 95 | } 96 | 97 | 98 | // after model is created, you can now train the model 99 | function trainModel() { 100 | app.models.train("pets").then( 101 | (response) => { 102 | console.log(response); 103 | }, 104 | (error) => { 105 | console.error(error); 106 | } 107 | ); 108 | } 109 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Clarifai JavaScript Stater 6 | 7 | 8 | 9 | 10 | 51 | 52 | 87 | 88 | 89 |
 Model Tester!
90 |
Model
91 | 92 |
93 | General
94 | Food
95 | NSFW
96 | Travel
97 | Wedding
98 | Color
99 | Apparel
100 | Faces
101 | Demographic
102 | Celebrity
103 | Logo
104 | Focus
105 | Moderation
106 | Portrait Quality
107 | Landscape Quality
108 | Textures & Patterns
109 | Custom  
110 |
111 |
112 | 124 | 125 | 126 | 127 |

128 | 129 | 140 | 141 | 142 |
143 | 144 |
145 |
146 | 147 |
148 | 149 |
150 |
151 |
152 |
153 | 154 | 155 | 156 | -------------------------------------------------------------------------------- /keys.js: -------------------------------------------------------------------------------- 1 | var myApiKey = 'YOUR API KEY HERE'; 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "javascript-starter", 3 | "version": "1.0.0", 4 | "description": "Clarifai V2 JavaScript Starter", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/Clarifai/javascript-starter.git" 12 | }, 13 | "author": "", 14 | "license": "Apache-2.0", 15 | "bugs": { 16 | "url": "https://github.com/Clarifai/javascript-starter/issues" 17 | }, 18 | "homepage": "https://github.com/Clarifai/javascript-starter#readme", 19 | "dependencies": { 20 | "clarifai": "^2.0.18", 21 | "filereader": "^0.10.3" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /predict.js: -------------------------------------------------------------------------------- 1 | /* 2 | Purpose: Pass information to other helper functions after a user clicks 'Predict' 3 | Args: 4 | value - Actual filename or URL 5 | source - 'url' or 'file' 6 | */ 7 | function predict_click(value, source) { 8 | // first grab current index 9 | var index = document.getElementById("hidden-counter").value; 10 | 11 | // Div Stuff 12 | if(index > 1) { 13 | createNewDisplayDiv(index); 14 | } 15 | 16 | if(source === "url") { 17 | document.getElementById("img_preview" + index).src = value; 18 | doPredict({ url: value }); 19 | 20 | // Div Stuff 21 | createHiddenDivs("url", value); 22 | } 23 | 24 | else if(source === "file") { 25 | var preview = document.querySelector("#img_preview" + index); 26 | var file = document.querySelector("input[type=file]").files[0]; 27 | var reader = new FileReader(); 28 | 29 | // load local file picture 30 | reader.addEventListener("load", function () { 31 | preview.src = reader.result; 32 | var localBase64 = reader.result.split("base64,")[1]; 33 | doPredict({ base64: localBase64 }); 34 | 35 | // Div Stuff 36 | createHiddenDivs("base64", localBase64); 37 | 38 | }, false); 39 | 40 | if (file) { 41 | reader.readAsDataURL(file); 42 | } 43 | } 44 | } 45 | 46 | /* 47 | Purpose: Does a v2 prediction based on user input 48 | Args: 49 | value - Either {url : urlValue} or { base64 : base64Value } 50 | */ 51 | function doPredict(value) { 52 | 53 | var modelID = getSelectedModel(); 54 | 55 | app.models.predict(modelID, value).then( 56 | 57 | function(response) { 58 | console.log(response); 59 | var conceptNames = ""; 60 | var tagArray, regionArray; 61 | var tagCount = 0; 62 | var modelName = response.rawData.outputs[0].model.name; 63 | var modelNameShort = modelName.split("-")[0]; 64 | var modelHeader = '' + capitalize(modelNameShort) + ' Model'; 65 | 66 | // Check for regions models first 67 | if(response.rawData.outputs[0].data.hasOwnProperty("regions")) { 68 | regionArray = response.rawData.outputs[0].data.regions; 69 | 70 | // Regions are found, so iterate through all of them 71 | for(var i = 0; i < regionArray.length; i++) { 72 | conceptNames += "Result " + (i+1) + ""; 73 | 74 | // Demographic has separate sub-arrays 75 | if(modelName == "demographics") { 76 | var ageArray = regionArray[i].data.face.age_appearance.concepts; 77 | var ethnicArray = regionArray[i].data.face.multicultural_appearance.concepts; 78 | var genderArray = regionArray[i].data.face.gender_appearance.concepts; 79 | 80 | // Age Header 81 | conceptNames += '
Age Appearance'; 82 | 83 | // print top 5 ages 84 | for(var a = 0; a < 5; a++) { 85 | conceptNames += '
  • ' + ageArray[a].name + ': ' + ageArray[a].value + '
  • '; 86 | } 87 | 88 | // Ethnicity Header 89 | conceptNames += 'Multicultural Appearance'; 90 | 91 | // print top 3 ethnicities 92 | for(var e = 0; e < 3; e++) { 93 | conceptNames += '
  • ' + ethnicArray[e].name + ': ' + ethnicArray[e].value + '
  • '; 94 | } 95 | 96 | // Gender Header 97 | conceptNames += 'Gender Appearance'; 98 | 99 | // print gender 100 | conceptNames += '
  • ' + genderArray[0].name + ': ' + genderArray[0].value + '
  • '; 101 | } 102 | 103 | // For faces just print bounding boxes 104 | else if(modelName == "face-v1.3") { 105 | // Top Row 106 | conceptNames += '
  • Top Row: ' + regionArray[i].region_info.bounding_box.top_row + '
  • '; 107 | conceptNames += '
  • Left Column: ' + regionArray[i].region_info.bounding_box.left_col + '
  • '; 108 | conceptNames += '
  • Bottom Row: ' + regionArray[i].region_info.bounding_box.bottom_row + '
  • '; 109 | conceptNames += '
  • Right Column: ' + regionArray[i].region_info.bounding_box.right_col + '
  • '; 110 | } 111 | 112 | // Celebrity 113 | else if(modelName == "celeb-v1.3") { 114 | tagArray = regionArray[i].data.face.identity.concepts; 115 | 116 | // Print first 10 results 117 | for(var c=0; c < 10; c++) { 118 | conceptNames += '
  • ' + tagArray[c].name + ': ' + tagArray[c].value + '
  • '; 119 | } 120 | } 121 | 122 | // Logos 123 | else if(modelName == "logo") { 124 | // Print all results 125 | conceptNames += '
    Logo'; 126 | conceptNames += '
  • ' + regionArray[i].data.concepts[0].name + ': ' + regionArray[i].data.concepts[0].value + '
  • '; 127 | conceptNames += '
    Location'; 128 | conceptNames += '
  • Top Row: ' + regionArray[i].region_info.bounding_box.top_row + '
  • '; 129 | conceptNames += '
  • Left Column: ' + regionArray[i].region_info.bounding_box.left_col + '
  • '; 130 | conceptNames += '
  • Bottom Row: ' + regionArray[i].region_info.bounding_box.bottom_row + '
  • '; 131 | conceptNames += '
  • Right Column: ' + regionArray[i].region_info.bounding_box.right_col + '
  • '; 132 | } 133 | 134 | // Focus 135 | else if(modelName == "focus") { 136 | // Print total focus score and all regions with focus 137 | conceptNames += '
    Focus Region'; 138 | conceptNames += '
  • Top Row: ' + regionArray[i].region_info.bounding_box.top_row + '
  • '; 139 | conceptNames += '
  • Left Column: ' + regionArray[i].region_info.bounding_box.left_col + '
  • '; 140 | conceptNames += '
  • Bottom Row: ' + regionArray[i].region_info.bounding_box.bottom_row + '
  • '; 141 | conceptNames += '
  • Right Column: ' + regionArray[i].region_info.bounding_box.right_col + '
  • '; 142 | 143 | if(i === regionArray.length - 1) { 144 | conceptNames += '



  • Overall Focus: ' + response.rawData.outputs[0].data.focus.value + '


  • '; 145 | } 146 | } 147 | 148 | tagCount+=10; 149 | } 150 | } 151 | 152 | // Color Model 153 | else if(modelName === "color") { 154 | conceptNames += 'Colors'; 155 | tagArray = response.rawData.outputs[0].data.colors; 156 | 157 | for (var col = 0; col < tagArray.length; col++) { 158 | conceptNames += '
  • ' + tagArray[col].w3c.name + ': ' + tagArray[col].value + '
  • '; 159 | } 160 | 161 | tagCount=tagArray.length; 162 | } 163 | 164 | // Generic tag response models 165 | else if(response.rawData.outputs[0].data.hasOwnProperty("concepts")) { 166 | tagArray = response.rawData.outputs[0].data.concepts; 167 | 168 | for (var other = 0; other < tagArray.length; other++) { 169 | conceptNames += '
  • ' + tagArray[other].name + ': ' + tagArray[other].value + '
  • '; 170 | } 171 | 172 | tagCount=tagArray.length; 173 | } 174 | 175 | // Bad region request 176 | else { 177 | if(modelName != "logo" && modelName != "focus") { 178 | $('#concepts').html("

    No Faces Detected!"); 179 | } 180 | else if(modelName == "logo") { 181 | $('#concepts').html("

    No Logos Detected!"); 182 | } 183 | else { 184 | $('#concepts').html("

    No Focus Regions Detected!"); 185 | } 186 | return; 187 | } 188 | 189 | var columnCount = tagCount / 10; 190 | 191 | // Focus gets one more column 192 | if(modelName == "focus") { 193 | columnCount += 1; 194 | } 195 | 196 | conceptNames = ''; 199 | conceptNames = modelHeader + conceptNames; 200 | 201 | $('#concepts').html(conceptNames); 202 | 203 | document.getElementById("add-image-button").style.visibility = "visible"; 204 | }, 205 | function(err) { 206 | console.log(err); 207 | } 208 | ); 209 | } 210 | 211 | /* 212 | Purpose: Return a back-end model id based on current user selection 213 | Returns: 214 | Back-end model id 215 | */ 216 | function getSelectedModel() { 217 | var model = document.querySelector('input[name = "model"]:checked').value; 218 | 219 | if(model === "general") { 220 | return Clarifai.GENERAL_MODEL; 221 | } 222 | 223 | else if(model === "food") { 224 | return Clarifai.FOOD_MODEL; 225 | } 226 | 227 | else if(model === "NSFW") { 228 | return Clarifai.NSFW_MODEL; 229 | } 230 | 231 | else if(model === "travel") { 232 | return Clarifai.TRAVEL_MODEL; 233 | } 234 | 235 | else if(model === "wedding") { 236 | return Clarifai.WEDDINGS_MODEL; 237 | } 238 | 239 | else if(model === "color") { 240 | return Clarifai.COLOR_MODEL; 241 | } 242 | 243 | else if(model === "demographic") { 244 | return Clarifai.DEMOGRAPHICS_MODEL; 245 | } 246 | 247 | else if(model === "logo") { 248 | return Clarifai.LOGO_MODEL; 249 | } 250 | 251 | else if(model === "apparel") { 252 | return "e0be3b9d6a454f0493ac3a30784001ff"; 253 | } 254 | 255 | else if(model === "faces") { 256 | return Clarifai.FACE_DETECT_MODEL; 257 | } 258 | 259 | else if(model == "focus") { 260 | return Clarifai.FOCUS_MODEL; 261 | } 262 | 263 | else if(model === "celebrity") { 264 | return "e466caa0619f444ab97497640cefc4dc"; 265 | } 266 | 267 | else if(model === "moderation") { 268 | return "d16f390eb32cad478c7ae150069bd2c6"; 269 | } 270 | 271 | else if(model === "portrait") { 272 | return "de9bd05cfdbf4534af151beb2a5d0953"; 273 | } 274 | 275 | else if(model === "landscape") { 276 | return "bec14810deb94c40a05f1f0eb3c91403"; 277 | } 278 | 279 | else if(model == "texturespatterns") { 280 | return "fbefb47f9fdb410e8ce14f24f54b47ff"; 281 | } 282 | 283 | else if(model === "custom") { 284 | var e = document.getElementById("custom_models_dropdown"); 285 | return e.options[e.selectedIndex].value; 286 | } 287 | } 288 | 289 | /* 290 | Purpose: Add an image to an application after user clicks button 291 | Args: 292 | index - # of the image in the session 293 | */ 294 | function addImageToApp(index) { 295 | var imgType = document.getElementById("hidden-type" + index).value; 296 | var imgValue = document.getElementById("hidden-val" + index).value; 297 | 298 | if(imgType === "url") { 299 | app.inputs.create({ 300 | url: imgValue 301 | }).then( 302 | function(response) { 303 | alert("Image successfully added!"); 304 | }, 305 | function(err) { 306 | alert("Error Adding Image. Check to see if it is a duplicate."); 307 | } 308 | ); 309 | } 310 | 311 | else if(imgType === "base64") { 312 | app.inputs.create({ 313 | base64: imgValue 314 | }).then( 315 | function(response) { 316 | alert("Image successfully added!"); 317 | }, 318 | function(err) { 319 | alert("Error Adding Image. Check to see if it is a duplicate."); 320 | } 321 | ); 322 | } 323 | } 324 | 325 | /* 326 | Purpose: Create a dynamic div to store entire user session 327 | Args: 328 | index - # of the image in the session 329 | */ 330 | function createNewDisplayDiv(index) { 331 | var mainDiv = document.getElementById("predictions"); 332 | 333 | var elem = document.createElement('div'); 334 | elem.innerHTML = 335 | '
    \ 336 | \ 337 |
    \ 338 | \ 341 |
    \ 342 |
    \ 343 |
    '; 344 | 345 | mainDiv.innerHTML = elem.innerHTML + mainDiv.innerHTML; 346 | } 347 | 348 | /* 349 | Purpose: Creates hidden Div elements to store info of each picture uploaded 350 | Args: 351 | urlOrBase64 - binary variable to store the type of image 352 | source - the actual URL string or the base64 353 | */ 354 | function createHiddenDivs(urlOrBase64, source) { 355 | // first grab current index 356 | var index = document.getElementById("hidden-counter").value; 357 | 358 | // type 359 | var input1 = document.createElement("input"); 360 | input1.setAttribute("type", "hidden"); 361 | input1.setAttribute("id", "hidden-type"+index); 362 | input1.setAttribute("name", "hidden-type"+index); 363 | input1.setAttribute("value", urlOrBase64); 364 | 365 | // value 366 | var input2 = document.createElement("input"); 367 | input2.setAttribute("type", "hidden"); 368 | input2.setAttribute("id", "hidden-val"+index); 369 | input2.setAttribute("name", "hidden-val"+index); 370 | input2.setAttribute("value", source); 371 | 372 | // add new inputs to page 373 | document.getElementsByTagName('body')[0].appendChild(input1); 374 | document.getElementsByTagName('body')[0].appendChild(input2); 375 | 376 | // increment index 377 | document.getElementById("hidden-counter").value = parseInt(index)+1; 378 | } 379 | 380 | /* 381 | Purpose: Return a capitalized String 382 | Args: 383 | s - A String 384 | */ 385 | function capitalize(s) 386 | { 387 | return s[0].toUpperCase() + s.slice(1); 388 | } 389 | --------------------------------------------------------------------------------