forked from sightmachine/SimpleCV
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathImageClass.py
More file actions
13609 lines (10218 loc) · 465 KB
/
ImageClass.py
File metadata and controls
13609 lines (10218 loc) · 465 KB
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
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# Load required libraries
from SimpleCV.base import *
from SimpleCV.Color import *
from SimpleCV.LineScan import *
from numpy import int32
from numpy import uint8
from EXIF import *
if not init_options_handler.headless:
import pygame as pg
import scipy.ndimage as ndimage
import scipy.stats.stats as sss #for auto white balance
import scipy.cluster.vq as scv
import scipy.linalg as nla # for linear algebra / least squares
import math # math... who does that
import copy # for deep copy
#import scipy.stats.mode as spsmode
class ColorSpace:
"""
**SUMMARY**
The colorspace class is used to encapsulate the color space of a given image.
This class acts like C/C++ style enumerated type.
See: http://stackoverflow.com/questions/2122706/detect-color-space-with-opencv
"""
UNKNOWN = 0
BGR = 1
GRAY = 2
RGB = 3
HLS = 4
HSV = 5
XYZ = 6
YCrCb = 7
class ImageSet(list):
"""
**SUMMARY**
This is an abstract class for keeping a list of images. It has a few
advantages in that you can use it to auto load data sets from a directory
or the net.
Keep in mind it inherits from a list too, so all the functionality a
normal python list has this will too.
**EXAMPLES**
>>> imgs = ImageSet()
>>> imgs.download("ninjas")
>>> imgs.show(ninjas)
or you can load a directory path:
>>> imgs = ImageSet('/path/to/imgs/')
>>> imgs.show()
This will download and show a bunch of random ninjas. If you want to
save all those images locally then just use:
>>> imgs.save()
You can also load up the sample images that come with simplecv as:
>>> imgs = ImageSet('samples')
>>> imgs.filelist
>>> logo = imgs.find('simplecv.png')
**TO DO**
Eventually this should allow us to pull image urls / paths from csv files.
The method also allow us to associate an arbitraty bunch of data with each
image, and on load/save pickle that data or write it to a CSV file.
"""
filelist = None
def __init__(self, directory = None):
if not directory:
return
if isinstance(directory,list):
if isinstance(directory[0], Image):
super(ImageSet,self).__init__(directory)
elif isinstance(directory[0], str) or isinstance(directory[0], unicode):
super(ImageSet,self).__init__(map(Image, directory))
elif directory.lower() == 'samples' or directory.lower() == 'sample':
pth = LAUNCH_PATH
pth = os.path.realpath(pth)
directory = os.path.join(pth, 'sampleimages')
self.load(directory)
else:
self.load(directory)
def download(self, tag=None, number=10, size='thumb'):
"""
**SUMMARY**
This function downloads images from Google Image search based
on the tag you provide. The number is the number of images you
want to have in the list. Valid values for size are 'thumb', 'small',
'medium', 'large' or a tuple of exact dimensions i.e. (640,480).
Note that 'thumb' is exceptionally faster than others.
.. Warning::
This requires the python library Beautiful Soup to be installed
http://www.crummy.com/software/BeautifulSoup/
**PARAMETERS**
* *tag* - A string of tag values you would like to download.
* *number* - An integer of the number of images to try and download.
* *size* - the size of the images to download. Valid options a tuple
of the exact size or a string of the following approximate sizes:
* thumb ~ less than 128x128
* small ~ approximately less than 640x480 but larger than 128x128
* medium ~ approximately less than 1024x768 but larger than 640x480.
* large ~ > 1024x768
**RETURNS**
Nothing - but caches local copy of images.
**EXAMPLE**
>>> imgs = ImageSet()
>>> imgs.download("ninjas")
>>> imgs.show(ninjas)
"""
try:
from BeautifulSoup import BeautifulSoup
except:
print "You need to install Beatutiul Soup to use this function"
print "to install you can use:"
print "easy_install beautifulsoup"
return
INVALID_SIZE_MSG = """I don't understand what size images you want.
Valid options: 'thumb', 'small', 'medium', 'large'
or a tuple of exact dimensions i.e. (640,480)."""
if isinstance(size, basestring):
size = size.lower()
if size == 'thumb':
size_param = ''
elif size == 'small':
size_param = '&tbs=isz:s'
elif size == 'medium':
size_param = '&tbs=isz:m'
elif size == 'large':
size_param = '&tbs=isz:l'
else:
print INVALID_SIZE_MSG
return None
elif type(size) == tuple:
width, height = size
size_param = '&tbs=isz:ex,iszw:' + str(width) + ',iszh:' + str(height)
else:
print INVALID_SIZE_MSG
return None
# Used to extract imgurl parameter value from a URL
imgurl_re = re.compile('(?<=(&|\?)imgurl=)[^&]*((?=&)|$)')
add_set = ImageSet()
candidate_count = 0
while len(add_set) < number:
opener = urllib2.build_opener()
opener.addheaders = [('User-agent', 'Mozilla/5.0')]
url = ("http://www.google.com/search?tbm=isch&q=" + urllib2.quote(tag) +
size_param + "&start=" + str(candidate_count))
page = opener.open(url)
soup = BeautifulSoup(page)
img_urls = []
# Gets URLs of the thumbnail images
if size == 'thumb':
imgs = soup.findAll('img')
for img in imgs:
dl_url = str(dict(img.attrs)['src'])
img_urls.append(dl_url)
# Gets the direct image URLs
else:
for link_tag in soup.findAll('a', {'href': re.compile('imgurl=')}):
dirty_url = link_tag.get('href') # URL to an image as given by Google Images
dl_url = str(re.search(imgurl_re, dirty_url).group()) # The direct URL to the image
img_urls.append(dl_url)
for dl_url in img_urls:
try:
add_img = Image(dl_url, verbose=False)
# Don't know a better way to check if the image was actually returned
if add_img.height <> 0 and add_img.width <> 0:
add_set.append(add_img)
except:
#do nothing
None
if len(add_set) >= number:
break
self.extend(add_set)
def upload(self,dest,api_key=None,api_secret=None, verbose = True):
"""
**SUMMARY**
Uploads all the images to imgur or flickr or dropbox. In verbose mode URL values are printed.
**PARAMETERS**
* *api_key* - a string of the API key.
* *api_secret* - (required only for flickr and dropbox ) a string of the API secret.
* *verbose* - If verbose is true all values are printed to the screen
**RETURNS**
if uploading is successful
- Imgur return the original image URL on success and None if it fails.
- Flick returns True on success, else returns False.
- dropbox returns True on success.
**EXAMPLE**
TO upload image to imgur::
>>> imgset = ImageSet("/home/user/Desktop")
>>> result = imgset.upload( 'imgur',"MY_API_KEY1234567890" )
>>> print "Uploaded To: " + result[0]
To upload image to flickr::
>>> imgset.upload('flickr','api_key','api_secret')
>>> imgset.upload('flickr') #Once the api keys and secret keys are cached.
To upload image to dropbox::
>>> imgset.upload('dropbox','api_key','api_secret')
>>> imgset.upload('dropbox') #Once the api keys and secret keys are cached.
**NOTES**
.. Warning::
This method requires two packages to be installed
-PyCurl
-flickr api.
-dropbox
.. Warning::
You must supply your own API key.
Find more about API keys:
- http://imgur.com/register/api_anon
- http://www.flickr.com/services/api/misc.api_keys.html
- https://www.dropbox.com/developers/start/setup#python
"""
try :
for i in self:
i.upload(dest,api_key,api_secret, verbose)
return True
except :
return False
def show(self, showtime = 0.25):
"""
**SUMMARY**
This is a quick way to show all the items in a ImageSet.
The time is in seconds. You can also provide a decimal value, so
showtime can be 1.5, 0.02, etc.
to show each image.
**PARAMETERS**
* *showtime* - the time, in seconds, to show each image in the set.
**RETURNS**
Nothing.
**EXAMPLE**
>>> imgs = ImageSet()
>>> imgs.download("ninjas")
>>> imgs.show()
"""
for i in self:
i.show()
time.sleep(showtime)
def _get_app_ext(self, loops=0):
""" Application extention. Part that secifies amount of loops.
if loops is 0, if goes on infinitely.
"""
bb = "\x21\xFF\x0B" # application extension
bb += "NETSCAPE2.0"
bb += "\x03\x01"
if loops == 0:
loops = 2**16-1
bb += int_to_bin(loops)
bb += '\x00' # end
return bb
def _get_graphics_control_ext(self, duration=0.1):
""" Graphics Control Extension. A sort of header at the start of
each image. Specifies transparancy and duration. """
bb = '\x21\xF9\x04'
bb += '\x08' # no transparency
bb += int_to_bin( int(duration*100) ) # in 100th of seconds
bb += '\x00' # no transparent color
bb += '\x00' # end
return bb
def _write_gif(self, filename, duration=0.1, loops=0, dither=1):
""" Given a set of images writes the bytes to the specified stream.
"""
frames = 0
previous = None
fp = open(filename, 'wb')
if not PIL_ENABLED:
logger.warning("Need PIL to write animated gif files.")
return
converted = []
for img in self:
if not isinstance(img,pil.Image):
pil_img = img.getPIL()
else:
pil_img = img
converted.append((pil_img.convert('P',dither=dither), img._get_header_anim()))
try:
for img, header_anim in converted:
if not previous:
# gather data
palette = getheader(img)[1]
data = getdata(img)
imdes, data = data[0], data[1:]
header = header_anim
appext = self._get_app_ext(loops)
graphext = self._get_graphics_control_ext(duration)
# write global header
fp.write(header)
fp.write(palette)
fp.write(appext)
# write image
fp.write(graphext)
fp.write(imdes)
for d in data:
fp.write(d)
else:
# gather info (compress difference)
data = getdata(img)
imdes, data = data[0], data[1:]
graphext = self._get_graphics_control_ext(duration)
# write image
fp.write(graphext)
fp.write(imdes)
for d in data:
fp.write(d)
previous = img.copy()
frames = frames + 1
fp.write(";") # end gif
finally:
fp.close()
return frames
def save(self, destination=None, dt=0.2, verbose = False, displaytype=None):
"""
**SUMMARY**
This is a quick way to save all the images in a data set.
Or to Display in webInterface.
If you didn't specify a path one will randomly be generated.
To see the location the files are being saved to then pass
verbose = True.
**PARAMETERS**
* *destination* - path to which images should be saved, or name of gif
* file. If this ends in .gif, the pictures will be saved accordingly.
* *dt* - time between frames, for creating gif files.
* *verbose* - print the path of the saved files to the console.
* *displaytype* - the method use for saving or displaying images.
valid values are:
* 'notebook' - display to the ipython notebook.
* None - save to a temporary file.
**RETURNS**
Nothing.
**EXAMPLE**
>>> imgs = ImageSet()
>>> imgs.download("ninjas")
>>> imgs.save(destination="ninjas_folder", verbose=True)
>>> imgs.save(destination="ninjas.gif", verbose=True)
"""
if displaytype=='notebook':
try:
from IPython.core.display import Image as IPImage
except ImportError:
print "You need IPython Notebooks to use this display mode"
return
from IPython.core import display as Idisplay
for i in self:
tf = tempfile.NamedTemporaryFile(suffix=".png")
loc = tf.name
tf.close()
i.save(loc)
Idisplay.display(IPImage(filename=loc))
return
else:
if destination:
if destination.endswith(".gif"):
return self._write_gif(destination, dt)
else:
for i in self:
i.save(path=destination, temp=True, verbose=verbose)
else:
for i in self:
i.save(verbose=verbose)
def showPaths(self):
"""
**SUMMARY**
This shows the file paths of all the images in the set.
If they haven't been saved to disk then they will not have a filepath
**RETURNS**
Nothing.
**EXAMPLE**
>>> imgs = ImageSet()
>>> imgs.download("ninjas")
>>> imgs.save(verbose=True)
>>> imgs.showPaths()
**TO DO**
This should return paths as a list too.
"""
for i in self:
print i.filename
def _read_gif(self, filename):
""" read_gif(filename)
Reads images from an animated GIF file. Returns the number of images loaded.
"""
if not PIL_ENABLED:
return
elif not os.path.isfile(filename):
return
pil_img = pil.open(filename)
pil_img.seek(0)
pil_images = []
try:
while True:
pil_images.append(pil_img.copy())
pil_img.seek(pil_img.tell()+1)
except EOFError:
pass
loaded = 0
for img in pil_images:
self.append(Image(img))
loaded += 1
return loaded
def load(self, directory = None, extension = None, sort_by=None):
"""
**SUMMARY**
This function loads up files automatically from the directory you pass
it. If you give it an extension it will only load that extension
otherwise it will try to load all know file types in that directory.
extension should be in the format:
extension = 'png'
**PARAMETERS**
* *directory* - The path or directory from which to load images.
* *extension* - The extension to use. If none is given png is the default.
* *sort_by* - Sort the directory based on one of the following parameters passed as strings.
* *time* - the modification time of the file.
* *name* - the name of the file.
* *size* - the size of the file.
The default behavior is to leave the directory unsorted.
**RETURNS**
The number of images in the image set.
**EXAMPLE**
>>> imgs = ImageSet()
>>> imgs.load("images/faces")
>>> imgs.load("images/eyes", "png")
"""
if not directory:
logger.warning("You need to give a directory to load files from.")
return
if not os.path.exists(directory):
logger.warning( "Invalid image path given.")
return
if extension:
extension = "*." + extension
formats = [os.path.join(directory, extension)]
else:
formats = [os.path.join(directory, x) for x in IMAGE_FORMATS]
file_set = [glob.glob(p) for p in formats]
full_set = []
for f in file_set:
for i in f:
full_set.append(i)
file_set = full_set
if(sort_by is not None):
if( sort_by.lower() == "time"):
file_set = sorted(file_set,key=os.path.getmtime)
if( sort_by.lower() == "name"):
file_set = sorted(file_set)
if( sort_by.lower() == "size"):
file_set = sorted(file_set,key=os.path.getsize)
self.filelist = dict()
for i in file_set:
tmp = None
try:
tmp = Image(i)
if( tmp is not None and tmp.width > 0 and tmp.height > 0):
if sys.platform.lower() == 'win32' or sys.platform.lower() == 'win64':
self.filelist[tmp.filename.split('\\')[-1]] = tmp
else:
self.filelist[tmp.filename.split('/')[-1]] = tmp
self.append(tmp)
except:
continue
return len(self)
def standardize(self,width,height):
"""
**SUMMARY**
Resize every image in the set to a standard size.
**PARAMETERS**
* *width* - the width that we want for every image in the set.
* *height* - the height that we want for every image in the set.
**RETURNS**
A new image set where every image in the set is scaled to the desired size.
**EXAMPLE**
>>>> iset = ImageSet("./b/")
>>>> thumbnails = iset.standardize(64,64)
>>>> for t in thumbnails:
>>>> t.show()
"""
retVal = ImageSet()
for i in self:
retVal.append(i.resize(width,height))
return retVal
def dimensions(self):
"""
**SUMMARY**
Return an np.array that are the width and height of every image in the image set.
**PARAMETERS**
--NONE--
**RETURNS**
A 2xN numpy array where N is the number of images in the set. The first column is
the width, and the second collumn is the height.
**EXAMPLE**
>>> iset = ImageSet("./b/")
>>> sz = iset.dimensions()
>>> np.max(sz[:,0]) # returns the largest width in the set.
"""
retVal = []
for i in self:
retVal.append((i.width,i.height))
return np.array(retVal)
def average(self, mode="first", size=(None,None)):
"""
**SUMMARY**
Casts each in the image set into a 32F image, averages them together and returns the results.
If the images are different sizes the method attempts to standarize them.
**PARAMETERS**
* *mode* -
* "first" - resize everything to the size of the first image.
* "max" - resize everything to be the max width and max height of the set.
* "min" - resize everything to be the min width and min height of the set.
* "average" - resize everything to be the average width and height of the set
* "fixed" - fixed, use the size tuple provided.
* *size* - if the mode is set to fixed use this tuple as the size of the resulting image.
**RETURNS**
Returns a single image that is the average of all the values.
**EXAMPLE**
>>> imgs = ImageSet()
>>> imgs.load("images/faces")
>>> result = imgs.average(mode="first")
>>> result.show()
**TODO**
* Allow the user to pass in an offset parameters that blit the images into the resutl.
"""
fw = 0
fh = 0
# figger out how we will handle everything
if( len(self) <= 0 ):
return ImageSet()
vals = self.dimensions()
if( mode.lower() == "first" ):
fw = self[0].width
fh = self[0].height
elif( mode.lower() == "fixed" ):
fw = size[0]
fh = size[1]
elif( mode.lower() == "max" ):
fw = np.max(vals[:,0])
fh = np.max(vals[:,1])
elif( mode.lower() == "min" ):
fw = np.min(vals[:,0])
fh = np.min(vals[:,1])
elif( mode.lower() == "average" ):
fw = int(np.average(vals[:,0]))
fh = int(np.average(vals[:,1]))
#determine if we really need to resize the images
t1 = np.sum(vals[:,0]-fw)
t2 = np.sum(vals[:,1]-fh)
if( t1 != 0 or t2 != 0 ):
resized = self.standardize(fw,fh)
else:
resized = self
# Now do the average calculation
accumulator = cv.CreateImage((fw,fh), cv.IPL_DEPTH_8U,3)
cv.Zero(accumulator)
alpha = float(1.0/len(resized))
beta = float((len(resized)-1.0)/len(resized))
for i in resized:
cv.AddWeighted(i.getBitmap(),alpha,accumulator,beta,0,accumulator)
retVal = Image(accumulator)
return retVal
def __getitem__(self,key):
"""
**SUMMARY**
Returns a ImageSet when sliced. Previously used to
return list. Now it is possible to ImageSet member
functions on sub-lists
"""
if type(key) is types.SliceType: #Or can use 'try:' for speed
return ImageSet(list.__getitem__(self, key))
else:
return list.__getitem__(self,key)
def __getslice__(self, i, j):
"""
Deprecated since python 2.0, now using __getitem__
"""
return self.__getitem__(slice(i,j))
class Image:
"""
**SUMMARY**
The Image class is the heart of SimpleCV and allows you to convert to and
from a number of source types with ease. It also has intelligent buffer
management, so that modified copies of the Image required for algorithms
such as edge detection, etc can be cached and reused when appropriate.
Image are converted into 8-bit, 3-channel images in RGB colorspace. It will
automatically handle conversion from other representations into this
standard format. If dimensions are passed, an empty image is created.
**EXAMPLE**
>>> i = Image("/path/to/image.png")
>>> i = Camera().getImage()
You can also just load the SimpleCV logo using:
>>> img = Image("simplecv")
>>> img = Image("logo")
>>> img = Image("logo_inverted")
>>> img = Image("logo_transparent")
Or you can load an image from a URL:
>>> img = Image("http://www.simplecv.org/image.png")
"""
width = 0 #width and height in px
height = 0
depth = 0
filename = "" #source filename
filehandle = "" #filehandle if used
camera = ""
_mLayers = []
_mDoHuePalette = False
_mPaletteBins = None
_mPalette = None
_mPaletteMembers = None
_mPalettePercentages = None
_barcodeReader = "" #property for the ZXing barcode reader
#these are buffer frames for various operations on the image
_bitmap = "" #the bitmap (iplimage) representation of the image
_matrix = "" #the matrix (cvmat) representation
_grayMatrix = "" #the gray scale (cvmat) representation -KAS
_graybitmap = "" #a reusable 8-bit grayscale bitmap
_equalizedgraybitmap = "" #the above bitmap, normalized
_blobLabel = "" #the label image for blobbing
_edgeMap = "" #holding reference for edge map
_cannyparam = (0, 0) #parameters that created _edgeMap
_pil = "" #holds a PIL object in buffer
_numpy = "" #numpy form buffer
_grayNumpy = "" # grayscale numpy for keypoint stuff
_colorSpace = ColorSpace.UNKNOWN #Colorspace Object
_pgsurface = ""
_cv2Numpy = None #numpy array for OpenCV >= 2.3
_cv2GrayNumpy = None #grayscale numpy array for OpenCV >= 2.3
_gridLayer = [None,[0,0]]#to store grid details | Format -> [gridIndex , gridDimensions]
#For DFT Caching
_DFT = [] #an array of 2 channel (real,imaginary) 64F images
#Keypoint caching values
_mKeyPoints = None
_mKPDescriptors = None
_mKPFlavor = "NONE"
#temp files
_tempFiles = []
#when we empty the buffers, populate with this:
_initialized_buffers = {
"_bitmap": "",
"_matrix": "",
"_grayMatrix": "",
"_graybitmap": "",
"_equalizedgraybitmap": "",
"_blobLabel": "",
"_edgeMap": "",
"_cannyparam": (0, 0),
"_pil": "",
"_numpy": "",
"_grayNumpy":"",
"_pgsurface": "",
"_cv2GrayNumpy": "",
"_cv2Numpy":""}
#The variables _uncroppedX and _uncroppedY are used to buffer the points when we crop the image.
_uncroppedX = 0
_uncroppedY = 0
def __repr__(self):
if len(self.filename) == 0:
fn = "None"
else:
fn = self.filename
return "<SimpleCV.Image Object size:(%d, %d), filename: (%s), at memory location: (%s)>" % (self.width, self.height, fn, hex(id(self)))
#initialize the frame
#parameters: source designation (filename)
#todo: handle camera/capture from file cases (detect on file extension)
def __init__(self, source = None, camera = None, colorSpace = ColorSpace.UNKNOWN,verbose=True, sample=False, cv2image=False, webp=False):
"""
**SUMMARY**
The constructor takes a single polymorphic parameter, which it tests
to see how it should convert into an RGB image. Supported types include:
**PARAMETERS**
* *source* - The source of the image. This can be just about anything, a numpy arrray, a file name, a width and height
tuple, a url. Certain strings such as "lenna" or "logo" are loaded automatically for quick testing.
* *camera* - A camera to pull a live image.
* *colorspace* - A default camera color space. If none is specified this will usually default to the BGR colorspace.
* *sample* - This is set to true if you want to load some of the included sample images without having to specify the complete path
**EXAMPLES**
>>> img = Image('simplecv')
>>> img = Image('test.png')
>>> img = Image('http://www.website.com/my_image.jpg')
>>> img.show()
**NOTES**
OpenCV: iplImage and cvMat types
Python Image Library: Image type
Filename: All opencv supported types (jpg, png, bmp, gif, etc)
URL: The source can be a url, but must include the http://
"""
self._mLayers = []
self.camera = camera
self._colorSpace = colorSpace
#Keypoint Descriptors
self._mKeyPoints = []
self._mKPDescriptors = []
self._mKPFlavor = "NONE"
#Pallete Stuff
self._mDoHuePalette = False
self._mPaletteBins = None
self._mPalette = None
self._mPaletteMembers = None
self._mPalettePercentages = None
#Temp files
self._tempFiles = []
#Check if need to load from URL
#(this can be made shorter)if type(source) == str and (source[:7].lower() == "http://" or source[:8].lower() == "https://"):
if isinstance(source, basestring) and (source.lower().startswith("http://") or source.lower().startswith("https://")):
#try:
# added spoofed user agent for images that are blocking bots (like wikipedia)
req = urllib2.Request(source, headers={'User-Agent' : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_4) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.54 Safari/536.5"})
img_file = urllib2.urlopen(req)
#except:
#if verbose:
#print "Couldn't open Image from URL:" + source
#return None
im = StringIO(img_file.read())
source = pil.open(im).convert("RGB")
#This section loads custom built-in images
if isinstance(source, basestring):
tmpname = source.lower()
if tmpname == "simplecv" or tmpname == "logo":
imgpth = os.path.join(LAUNCH_PATH, 'sampleimages','simplecv.png')
source = imgpth
elif tmpname == "simplecv_inverted" or tmpname == "inverted" or tmpname == "logo_inverted":
imgpth = os.path.join(LAUNCH_PATH, 'sampleimages','simplecv_inverted.png')
source = imgpth
elif tmpname == "lenna":
imgpth = os.path.join(LAUNCH_PATH, 'sampleimages','lenna.png')
source = imgpth
elif tmpname == "lyle":
imgpth = os.path.join(LAUNCH_PATH, 'sampleimages','LyleJune1973.png')
source = imgpth
elif tmpname == "parity":
choice = random.choice(['LyleJune1973.png','lenna.png'])
imgpth = os.path.join(LAUNCH_PATH, 'sampleimages',choice)
source = imgpth
elif sample:
imgpth = os.path.join(LAUNCH_PATH, 'sampleimages', source)
source = imgpth
if (type(source) == tuple):
w = int(source[0])
h = int(source[1])
source = cv.CreateImage((w,h), cv.IPL_DEPTH_8U, 3)
cv.Zero(source)
if (type(source) == cv.cvmat):
self._matrix = source
if((source.step/source.cols)==3): #this is just a guess
self._colorSpace = ColorSpace.BGR
elif((source.step/source.cols)==1):
self._colorSpace = ColorSpace.BGR
else:
self._colorSpace = ColorSpace.UNKNOWN
elif (type(source) == np.ndarray): #handle a numpy array conversion
if (type(source[0, 0]) == np.ndarray): #we have a 3 channel array
#convert to an iplimage bitmap
source = source.astype(np.uint8)
self._numpy = source
if not cv2image:
invertedsource = source[:, :, ::-1].transpose([1, 0, 2])
else:
# If the numpy array is from cv2, then it must not be transposed.
invertedsource = source
#invertedsource = source[:, :, ::-1].transpose([1, 0, 2]) # do not un-comment. breaks cv2 image support
self._bitmap = cv.CreateImageHeader((invertedsource.shape[1], invertedsource.shape[0]), cv.IPL_DEPTH_8U, 3)
cv.SetData(self._bitmap, invertedsource.tostring(),
invertedsource.dtype.itemsize * 3 * invertedsource.shape[1])