-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathNeurosisController.m
595 lines (451 loc) · 16.4 KB
/
NeurosisController.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
//
// NeurosisController.m
// Neurosis
//
// Created by Patrick B. Gibson on 01/04/08.
// Copyright 2008 __MyCompanyName__. All rights reserved.
//
#import "NeurosisController.h"
#import "PBGDefines.h"
#import "PBGImageAndTextCell.h"
#import "PBGTreeNode.h"
#import "PBGLesson.h"
#include <stdlib.h>
#define kMinOutlineViewSplit 125.0f
#define kSourcesColumnIdentifier @"SOURCES"
#define kInputsFolderIdentifier @"INPUTS"
#define kEducationFolderIdentifier @"EDUCATION"
#define kCameraIdentifier @"Camera"
#define kFilePrefix @"file://"
#define HTTP_PREFIX @"http://"
@implementation NeurosisController
- (id)init
{
self = [super init];
if (self != nil) {
lessonCount = 0;
contents = [[NSMutableArray alloc] init];
// Get the images for our camera and for our images
cameraIconImage = [[[NSWorkspace sharedWorkspace] iconForFile:@"/Applications/Image Capture.app"] copy];
[cameraIconImage setSize:NSMakeSize(16,16)];
[cameraIconImage retain];
photoIconImage = [[[NSWorkspace sharedWorkspace] iconForFileType:@"jpg"] retain];
[photoIconImage setSize:NSMakeSize(16,16)];
}
return self;
}
- (void)awakeFromNib
{
[[NSApplication sharedApplication] setDelegate:self];
[self setResolution:10.0];
// Apply our custom ImageAndTextCell for rendering the first column's cells
NSTableColumn *tableColumn = [sourceListView tableColumnWithIdentifier:kSourcesColumnIdentifier];
PBGImageAndTextCell *imageAndTextCell = [[[PBGImageAndTextCell alloc] init] autorelease];
[imageAndTextCell setEditable:YES];
[tableColumn setDataCell:imageAndTextCell];
cameraController = [[CameraController alloc] initWithNibName:@"Camera" bundle:nil];
lessonController = [[LessonController alloc] initWithNibName:@"Lesson" bundle:nil];
// Create our separator
separatorCell = [[PBGSeparatorCell alloc] init];
[separatorCell setEditable:NO];
// Create a child for the camera item and add it to the source list.
PBGTreeNode *inputs = [[PBGTreeNode alloc] initWithNodeType:SpecialFolderTreeNode
nodeTitle:kInputsFolderIdentifier
andNodeIcon:nil];
[self addNode:inputs atIndex:nil];
[inputs release];
PBGTreeNode *camera = [[PBGTreeNode alloc] initWithNodeType:CameraItemTreeNode
nodeTitle:kCameraIdentifier
andNodeIcon:cameraIconImage];
[self addNode:camera atIndex:nil];
[camera release];
NSArray *selection = [treeController selectionIndexPaths];
[treeController removeSelectionIndexPaths:selection];
PBGTreeNode *education = [[PBGTreeNode alloc] initWithNodeType:SpecialFolderTreeNode
nodeTitle:kEducationFolderIdentifier
andNodeIcon:nil];
[self addNode:education atIndex:nil];
[education release];
// Select the camera
NSUInteger indexes[2];
indexes[0] = 0;
indexes[1] = 0;
NSIndexPath *cameraSelection = [NSIndexPath indexPathWithIndexes:indexes length:2];
[treeController setSelectionIndexPath:cameraSelection];
// Preserve our selection
[treeController setSelectsInsertedObjects:NO];
// Register to hear notifications we care about
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter addObserver:self
selector:@selector(handleNewPicture:)
name:kPictureTakenNotification
object:nil];
[notificationCenter addObserver:self
selector:@selector(reconizeImage:)
name:kRecognizeImageNotification
object:nil];
}
#pragma mark - IBActions
- (IBAction)runNewANNSheet:(id)sender
{
[NSApp beginSheet:newANNSheet modalForWindow:window modalDelegate:self didEndSelector:NULL contextInfo:nil];
}
- (IBAction)cancelNewANNSheet:(id)sender
{
[newANNSheet orderOut:nil];
[NSApp endSheet:newANNSheet];
}
- (IBAction)createNewNeuralNetwork:(id)sender
{
// Create a dictionary with the info we need
NSMutableDictionary *dict = [[NSMutableDictionary alloc] initWithCapacity:1];
[dict setObject:[NSNumber numberWithDouble:resolution] forKey:kResolutionIdentifier];
// Make our notification here.
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter postNotificationName:kResolutionChangedNotification object:dict];
NSLog(@"Posting note with object:%@", dict);
if(!network) { [network release]; }
double height = [cameraController vElements];
double width = [cameraController hElements];
NSLog(@"Creating new neural net with size: %f", (width * height));
network = [[PBGNeuralNetwork alloc] initWithInputs:(width * height)
outputs:2
hiddenLayers:1];
//[network printDescription];
[newANNSheet orderOut:nil];
[NSApp endSheet:newANNSheet];
}
- (IBAction)reconizeImage:(id)sender
{
CIImage *image = [[cameraController grabFrame] retain];
NSArray *values = [PBGANNImageProcessor arrayRepresentationOfImage:image withPixellationFactor:resolution];
[network setStartingValues:values];
NSArray *outputs = [network computeOutputValues];
NSLog(@"Outputs were: %@", outputs);
// Match outputs to one of our tree items.
int firstValue;
if ([[outputs objectAtIndex:0] doubleValue] < 0.4)
firstValue = 0;
else
firstValue = 1;
int secondValue;
if ([[outputs objectAtIndex:1] doubleValue] < 0.4)
secondValue = 0;
else
secondValue = 1;
int meaningLocation = (secondValue * 2) + (firstValue);
PBGTreeNode *meaningNode = [[[contents objectAtIndex:1] children] objectAtIndex:meaningLocation];
[cameraController setRecognizedString:[[NSString stringWithString:@"I recognize a "] stringByAppendingString:[meaningNode nodeTitle]]];
[image release];
[values release];
}
#pragma mark - Notification Handling
- (void)handleNewPicture:(NSNotification *)notification
{
NSString *lessonMeaning = [[notification object] valueForKey:kMeaningIdentifier];
NSLog(@"Meaning: %@", lessonMeaning);
// Search for an existing folder with our lesson name
NSLog(@"Contents: %@", [[contents objectAtIndex:1] children]);
int index = [self containsExistingLessonOf:[[notification object] valueForKey:kMeaningIdentifier]];
// Create our new lesson
PBGLesson *newLesson = [[PBGLesson alloc] initWithImagePath:[[notification object] valueForKey:kFilePathIdentifier]
meaning:lessonMeaning
index:lessonCount++];
[newLesson addArrayRep:[[notification object] valueForKey:kImageAsArrayIdentifier]];
PBGTreeNode *lessonNode = [[PBGTreeNode alloc] initWithNodeType:LessonTreeNode
nodeTitle:lessonMeaning
andNodeIcon:photoIconImage];
[lessonNode setLesson:newLesson];
if (index < 0) { // Create a new tree folder
PBGTreeNode *folderNode = [[PBGTreeNode alloc] initWithNodeType:LessonFolderTreeNode
nodeTitle:lessonMeaning
andNodeIcon:photoIconImage
andIndex:lessonCount++];
// Add our new folder to the tree and our lesson to that folder
[self addNode:folderNode atIndex:nil];
[lessonNode setNodeTitle:[lessonMeaning stringByAppendingString:@" 1"]];
int foo = [[[contents objectAtIndex:1] children] indexOfObject:folderNode];
[self addNode:lessonNode atIndex:[NSNumber numberWithInt:foo]];
//[self trainUntilSmartForLesson:folderNode];
} else { // Add to an existing lesson
NSMutableArray *education = (NSMutableArray *) [[contents objectAtIndex:1] children];
int count = [[[education objectAtIndex:index] children] count];
[lessonNode setNodeTitle:[lessonMeaning stringByAppendingFormat:@" %d", (count + 1), nil]];
[self addNode:lessonNode atIndex:[NSNumber numberWithInt:index]];
//[self trainUntilSmartForLesson:[education objectAtIndex:index]];
}
[self trainUntilSmart];
}
- (void)trainUntilSmart
{
NSLog(@"Training");
NSMutableArray *education = (NSMutableArray *) [[contents objectAtIndex:1] children];
double sumSquaredError = 1;
while (sumSquaredError > 0.2) {
NSLog(@"sumSquared: %f", sumSquaredError);
double error = 0;
for (PBGTreeNode *folderNode in education) {
NSArray *expectedOuts = [folderNode expectedOutputsArray];
for (PBGTreeNode *lessonNode in [folderNode children]) {
PBGLesson *lesson = [lessonNode lesson];
[network setStartingValues:lesson.imageAsArray];
[network learnFromExpectedOutputs:expectedOuts];
double out1, out2;
NSArray *realOuts = [network computeOutputValues];
out1 = [[realOuts objectAtIndex:0] doubleValue];
out1 = [[expectedOuts objectAtIndex:0] intValue] - out1;
out2 = [[realOuts objectAtIndex:1] doubleValue];
out2 = [[expectedOuts objectAtIndex:1] intValue] - out2;
error += pow(out1, 2) + pow(out2, 2);
}
}
sumSquaredError = error;
}
}
- (int)containsExistingLessonOf:(NSString *)thing
{
NSMutableArray *education = (NSMutableArray *) [[contents objectAtIndex:1] children];
if ([education count] == 0) {
return -1;
} else {
int index = 0;
for (index = 0; index < [education count]; index++) {
if ([[[education objectAtIndex:index] nodeTitle] isEqualToString:thing])
return index;
}
}
return -1;
}
#pragma mark Tree Controller
- (void)addNode:(PBGTreeNode *)newNode atIndex:(NSNumber *)givenIndex
{
// Switch on the node type
PBGTreeNodeType newNodeType = [newNode nodeType];
switch(newNodeType) {
case SpecialFolderTreeNode:
[treeController insertObject:newNode atArrangedObjectIndexPath:[NSIndexPath indexPathWithIndex:[contents count]]];
break;
case CameraItemTreeNode:
NSLog(@"Loading camera.");
NSIndexPath *ip = [treeController selectionIndexPath];
ip = [ip indexPathByAddingIndex:[[[[treeController selectedObjects] objectAtIndex:0] children] count]];
NSLog(@"Index: %@", ip);
[treeController insertObject:newNode atArrangedObjectIndexPath:ip];
break;
case LessonFolderTreeNode:
NSLog(@"Adding Folder.");
NSUInteger findexes[2];
findexes[0] = 1;
findexes[1] = [[[contents objectAtIndex:1] children] count]; //However many are in education
NSIndexPath *findex = [NSIndexPath indexPathWithIndexes:findexes length:2];
[treeController insertObject:newNode atArrangedObjectIndexPath:findex];
break;
case LessonTreeNode:
NSLog(@"Adding Lesson.");
NSUInteger indexes[3];
indexes[0] = 1;
indexes[1] = [givenIndex intValue];
indexes[2] = [[[[[contents objectAtIndex:1] children] objectAtIndex:[givenIndex intValue]] children] count];
NSIndexPath *index = [NSIndexPath indexPathWithIndexes:indexes length:3];
[treeController insertObject:newNode atArrangedObjectIndexPath:index];
break;
default:
NSLog(@"Unknown nodeType passed to addNode:");
}
}
- (void)selectCamera
{
NSIndexPath *cameraPath = [[NSIndexPath indexPathWithIndex:0] indexPathByAddingIndex:0];
[treeController setSelectionIndexPath:cameraPath];
}
#pragma mark - Application Delegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
if (network == NULL)
[self runNewANNSheet:NULL];
}
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication
{
return YES;
}
#pragma mark - Split View Delegate
- (float)splitView:(NSSplitView *)splitView constrainMinCoordinate:(float)proposedCoordinate ofSubviewAt:(int)index
{
return proposedCoordinate + kMinOutlineViewSplit;
}
- (float)splitView:(NSSplitView *)splitView constrainMaxCoordinate:(float)proposedCoordinate ofSubviewAt:(int)index
{
return proposedCoordinate - kMinOutlineViewSplit;
}
// Keep the left split pane from resizing as the user moves the divider line.
- (void)splitView:(NSSplitView*)sender resizeSubviewsWithOldSize:(NSSize)oldSize
{
NSRect newFrame = [sender frame]; // get the new size of the whole splitView
NSView *left = [[sender subviews] objectAtIndex:0];
NSRect leftFrame = [left frame];
NSView *right = [[sender subviews] objectAtIndex:1];
NSRect rightFrame = [right frame];
CGFloat dividerThickness = [sender dividerThickness];
leftFrame.size.height = newFrame.size.height;
rightFrame.size.width = newFrame.size.width - leftFrame.size.width - dividerThickness;
rightFrame.size.height = newFrame.size.height;
rightFrame.origin.x = leftFrame.size.width + dividerThickness;
[left setFrame:leftFrame];
[right setFrame:rightFrame];
}
#pragma mark - KVC
- (double)resolution
{
return resolution;
}
- (void)setResolution:(double)r
{
resolution = r;
}
- (void)setContents:(NSArray*)newContents
{
if (contents != newContents)
{
[contents release];
contents = [[NSMutableArray alloc] initWithArray:newContents];
}
}
- (NSMutableArray *)contents
{
return contents;
}
#pragma mark - Learning notifications
//Listen for notifications that a picture was taken
#pragma mark - NSOutlineView delegate
- (BOOL)outlineView:(NSOutlineView *)outlineView shouldSelectItem:(id)item;
{
// don't allow special group nodes (Devices and Places) to be selected
PBGTreeNode* node = [item representedObject];
NSLog(@"node lesson: %@", [node lesson]);
BOOL shouldSelect = (![self isSpecialGroup:node] && ([node lesson] != nil)) || [node nodeType] == CameraItemTreeNode;
return shouldSelect;
}
- (NSCell *)outlineView:(NSOutlineView *)outlineView dataCellForTableColumn:(NSTableColumn *)tableColumn item:(id)item
{
NSCell* returnCell = [tableColumn dataCell];
if ([[tableColumn identifier] isEqualToString:kSourcesColumnIdentifier])
{
// we are being asked for the cell for the single and only column
PBGTreeNode* node = [item representedObject];
if ([node nodeIcon] == nil && [[node nodeTitle] length] == 0)
returnCell = separatorCell;
}
return returnCell;
}
- (BOOL)control:(NSControl *)control textShouldEndEditing:(NSText *)fieldEditor
{
if ([[fieldEditor string] length] == 0)
{
// don't allow empty node names
return NO;
}
else
{
return YES;
}
}
- (BOOL)outlineView:(NSOutlineView *)outlineView shouldEditTableColumn:(NSTableColumn *)tableColumn item:(id)item
{
BOOL result = YES;
item = [item representedObject];
if ([self isSpecialGroup:item])
{
result = NO; // don't allow special group nodes to be renamed
}
return result;
}
- (void)outlineView:(NSOutlineView *)olv willDisplayCell:(NSCell*)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
{
if ([[tableColumn identifier] isEqualToString:kSourcesColumnIdentifier] && [cell isKindOfClass:[PBGImageAndTextCell class]]) {
item = [item representedObject];
if (item) {
[(PBGImageAndTextCell*)cell setImage:[item nodeIcon]];
}
}
}
#pragma mark - View swapping
- (void)removeSubview
{
// empty selection
NSArray *subViews = [mainView subviews];
if ([subViews count] > 0)
{
[[subViews objectAtIndex:0] removeFromSuperview];
}
[mainView displayIfNeeded]; // we want the removed views to disappear right away
}
- (void)changeItemView
{
NSArray *selection = [treeController selectedObjects];
PBGTreeNode *node = [selection objectAtIndex:0];
//NSString *urlStr = [node urlString];
NSString *name = [node nodeTitle];
// If the user selected the camera view, switch to that unless we're already there.
if([name isEqualToString:kCameraIdentifier] && currentView != [cameraController view]) {
[self removeSubview];
currentView = [cameraController view];
[mainView addSubview:[cameraController view]];
} else { // One of our lessons is selected
[self removeSubview];
// Set up our lesson view stuff
[[lessonController imageView] setImage:[[node lesson] image]];
if ([node lesson])
[[lessonController meaningTextField] setStringValue:[[node lesson] meaning]];
currentView = [lessonController view];
[mainView addSubview:[lessonController view]];
}
}
- (void)outlineViewSelectionDidChange:(NSNotification *)notification
{
// ask the tree controller for the current selection
NSArray *selection = [treeController selectedObjects];
if ([selection count] > 1)
{
// multiple selection - clear the right side view
[self removeSubview];
currentView = nil;
}
else
{
if ([selection count] == 1)
{
// single selection
[self changeItemView];
}
else
{
// there is no current selection - no view to display
[self removeSubview];
currentView = nil;
}
}
}
-(BOOL)outlineView:(NSOutlineView*)outlineView isGroupItem:(id)item
{
if ([self isSpecialGroup:[item representedObject]])
{
return YES;
}
else
{
return NO;
}
}
- (BOOL)isSpecialGroup:(PBGTreeNode *)groupNode
{
return ([groupNode nodeIcon] == nil && ([[groupNode nodeTitle] isEqualToString:kInputsFolderIdentifier] ||
[[groupNode nodeTitle] isEqualToString:kEducationFolderIdentifier]));
}
- (void)dealloc
{
[contents release];
[cameraIconImage release];
[photoIconImage release];
[super dealloc];
}
@end