diff --git a/bin/__init__.pyc b/bin/__init__.pyc new file mode 100644 index 0000000..6875fb8 Binary files /dev/null and b/bin/__init__.pyc differ diff --git a/examples/__init__.pyc b/examples/__init__.pyc new file mode 100644 index 0000000..f7fa609 Binary files /dev/null and b/examples/__init__.pyc differ diff --git a/examples/histogram_display.py b/examples/histogram_display.py index df4f48a..b85e16c 100644 --- a/examples/histogram_display.py +++ b/examples/histogram_display.py @@ -11,24 +11,20 @@ The histogram calculation is performed using the OpenCV library. In order to simplify the process, an Histogram class has been created, which contains all necessary data for a proper output image creation. -The final output is an IplImage in this script. -To end the script, the user simply needs to hit a keyboard key with the input -image in the foreground. +The final output is a list of IplImages in this script. Each item is a graphical +representation of teh histogram result for one channel. -For now, only the grayscale function is available, but color images support -should be added soon. +To end the script, the user simply needs to hit a keyboard key with the input +image in the foreground. ''' import cv import tippy.statistics as st import tippy.display_operations as do -hist = st.Histogram() +img_name = "tippy/data/tippy.jpg" +img = cv.LoadImage(img_name, cv.CV_LOAD_IMAGE_COLOR) +hist = st.Histogram(img) -img_name = "tippy/data/gnu.jpg" -img = cv.LoadImage(img_name, cv.CV_LOAD_IMAGE_GRAYSCALE) -hist.compute_histogram(img) - -hist_img = hist.hist2image() - -do.display_single_image(hist_img, "Gray-Level histogram of the gnu image") +imgs = hist.to_images() # list of Red, Green, Blue channels +do.display_single_image(imgs[2], "Blue channel histogram of the tippy image") diff --git a/examples/region_growing.pyc b/examples/region_growing.pyc new file mode 100644 index 0000000..b10fe5b Binary files /dev/null and b/examples/region_growing.pyc differ diff --git a/tippy/__init__.pyc b/tippy/__init__.pyc new file mode 100644 index 0000000..8ec2d8d Binary files /dev/null and b/tippy/__init__.pyc differ diff --git a/tippy/basic_operations.pyc b/tippy/basic_operations.pyc new file mode 100644 index 0000000..266cac8 Binary files /dev/null and b/tippy/basic_operations.pyc differ diff --git a/tippy/display_operations.pyc b/tippy/display_operations.pyc new file mode 100644 index 0000000..fa0ad1f Binary files /dev/null and b/tippy/display_operations.pyc differ diff --git a/tippy/segmentations.pyc b/tippy/segmentations.pyc new file mode 100644 index 0000000..87f7ef7 Binary files /dev/null and b/tippy/segmentations.pyc differ diff --git a/tippy/statistics.py b/tippy/statistics.py index 19d6584..00c2d23 100644 --- a/tippy/statistics.py +++ b/tippy/statistics.py @@ -15,15 +15,16 @@ class Histogram(): This class is designed to contain an openCV class plus some more information to ease further operations. - NOTE : Supports only 1 channel / 8 bits images for now. + NOTE : Supports only 8 bits images for now. More should be added soon. """ - def __init__(self): + def __init__(self, img, bin_fact=cv.IPL_DEPTH_8U, min_range=0): self.cvhist = None self.min_range = 0 self.depth = cv.IPL_DEPTH_8U self.nb_bins = 0 self.channels = 0 + self.compute_histogram(img, bin_fact, min_range) def compute_histogram(self, img, bin_fact=cv.IPL_DEPTH_8U, min_range=0): """ @@ -35,12 +36,12 @@ class Histogram(): max_range is automatically calculated using the input image depth. max_range = (2^img_depth) - NOTE : Supports only 1 channel / 8 bits images for now. + NOTE : Supports only 8 bits images for now. More should be added soon. """ # TESTS try: - cv.GetSize(img) + dims = cv.GetSize(img) except TypeError: raise TypeError("(%s) img : IplImage expected!" % (sys._getframe().f_code.co_name)) @@ -49,10 +50,9 @@ class Histogram(): if not(img.depth == cv.IPL_DEPTH_8U): raise TypeError("(%s) 8U image expected!" % (sys._getframe().f_code.co_name)) - elif not(img.nChannels is 1): - raise TypeError("(%s) 1C image expected!" - % (sys._getframe().f_code.co_name)) - + if img.nChannels not in [1, 3]: + raise ValueError("(%s) 1 or 3 channels image expected!" + % (sys._getframe().f_code.co_name)) # bin_fact test if (bin_fact <= 0 or ((bin_fact % 2) != 0)): raise ValueError("(%s) Positive odd integer expected!" @@ -71,43 +71,104 @@ class Histogram(): self.depth = int(img.depth) max_range = pow(2, self.depth) self.ranges = [min_range, max_range] - + + img_list = [] + # preparing images depending on nChannels + if self.channels == 1: + img_list = [img] + elif self.channels == 3: + # TODO: change this into function + img_1 = cv.CreateImage(dims, cv.IPL_DEPTH_8U, 1) + img_2 = cv.CreateImage(dims, cv.IPL_DEPTH_8U, 1) + img_3 = cv.CreateImage(dims, cv.IPL_DEPTH_8U, 1) + + cv.Split(img, img_1, img_2, img_3, None) + img_list = [img_1, img_2, img_3] + + self.cvhist = self._compute_1ch_histogram(img_list) + + def _compute_1ch_histogram(self, img_list): + """ + DESIGNED FOR INTERNAL USE ONLY. + BE CAREFUL, NO VERIFICATIONS PERFORMED + """ dims = [self.nb_bins] all_ranges = [self.ranges] - self.cvhist = cv.CreateHist( dims, - cv.CV_HIST_ARRAY, - all_ranges, - uniform=1) - cv.CalcHist([img], self.cvhist) + hist_list = [] + for img in img_list: + hist = cv.CreateHist( dims, + cv.CV_HIST_ARRAY, + all_ranges, + uniform=1) + cv.CalcHist([img], hist) + + hist_list.append(hist) + + return hist_list - def hist2table(self): + def to_tables(self): """ - Transform an histogram into a list of values. + Transforms an histogram into a list of values. This way, it is easier to process for calculations. - NOTE : Supports only 1 channel / 8 bits images for now. - More should be added soon. + Returns a list of nChannels items. + + NOTE : Supports only 8 bits images for now. + More should be added soon. """ if self.nb_bins < 1: raise TypeError("(%s) Histogram expected!" % (sys._getframe().f_code.co_name)) histable = [] - for jj in range(self.nb_bins): - histable.append(self.cvhist.bins[jj]) - + for ii in range(self.channels): + hist_list = [] + for jj in range(self.nb_bins): + hist_list.append(self.cvhist[ii].bins[jj]) + histable.append(hist_list) return histable + + def to_images(self, scale_x=3, scale_y=3, y_range=64): + """ + Creates a graphical representation of an histogram. + Outputs a list of nChannels iplimages, one for each channel of the + histogram. + The images should have the same depth as the image used to create the + histogram. + """ + if ((type(scale_x) != int) or (scale_x <= 0)): + raise TypeError("(%s) Positive integer expected!" + % (sys._getframe().f_code.co_name)) - def hist2image(self, scale_x=3, scale_y=3, y_range=64): - """ - Creates an iplimage displaying histogram results after its computation - # TODO : might be used with histable too ? - """ - if self.nb_bins < 1: - raise TypeError("(%s) Histogram expected!" + if ((type(scale_y) != int) or (scale_y <= 0)): + raise TypeError("(%s) Positive integer expected!" + % (sys._getframe().f_code.co_name)) + + if ((type(y_range) != int) or (y_range <= 0)): + raise TypeError("(%s) Positive integer expected!" % (sys._getframe().f_code.co_name)) + images = [] + for jj in range(self.channels): + images.append(self.channel_to_image(jj + 1, + scale_x, + scale_y, + y_range)) + + return images + + def channel_to_image(self, chan, scale_x=3, scale_y=3, y_range=64): + """ + Creates an iplimage displaying histogram results after its computation + """ + if ((type(chan) != int) or (chan <= 0)): + raise TypeError("(%s) Positive integer expected!" + % (sys._getframe().f_code.co_name)) + elif chan > self.channels: + raise ValueError("(%s) Incoherent channel selected!" + % (sys._getframe().f_code.co_name)) + if ((type(scale_x) != int) or (scale_x <= 0)): raise TypeError("(%s) Positive integer expected!" % (sys._getframe().f_code.co_name)) @@ -121,15 +182,15 @@ class Histogram(): % (sys._getframe().f_code.co_name)) max_range = self.ranges[1] - 1 - (_, hist_max, _, _ ) = cv.GetMinMaxHistValue(self.cvhist) + (_, hist_max, _, _ ) = cv.GetMinMaxHistValue(self.cvhist[chan - 1]) hist_img = cv.CreateImage( (max_range*scale_x, y_range*scale_y), self.depth, - self.channels) + 1) cv.Zero(hist_img) # resets image values for i in range(max_range): # 0 to max_range - hist_value = cv.QueryHistValue_1D(self.cvhist, i) - next_value = cv.QueryHistValue_1D(self.cvhist, i+1) + hist_value = cv.QueryHistValue_1D(self.cvhist[chan - 1], i) + next_value = cv.QueryHistValue_1D(self.cvhist[chan - 1], i+1) pt1 = ( int(i*scale_x), int(y_range*scale_y)) pt2 = ( int(i*scale_x+ scale_x), @@ -141,4 +202,6 @@ class Histogram(): pts = (pt1, pt2, pt3, pt4) cv.FillConvexPoly(hist_img, pts, 255, lineType=8, shift=0) - return hist_img \ No newline at end of file + return hist_img + + \ No newline at end of file diff --git a/tippy/statistics.pyc b/tippy/statistics.pyc new file mode 100644 index 0000000..3f7187e Binary files /dev/null and b/tippy/statistics.pyc differ diff --git a/tippy/tests/__init__.pyc b/tippy/tests/__init__.pyc new file mode 100644 index 0000000..8d496ec Binary files /dev/null and b/tippy/tests/__init__.pyc differ diff --git a/tippy/tests/test_basic_operations.pyc b/tippy/tests/test_basic_operations.pyc new file mode 100644 index 0000000..c24cdd7 Binary files /dev/null and b/tippy/tests/test_basic_operations.pyc differ diff --git a/tippy/tests/test_display_operations.pyc b/tippy/tests/test_display_operations.pyc new file mode 100644 index 0000000..732bbb6 Binary files /dev/null and b/tippy/tests/test_display_operations.pyc differ diff --git a/tippy/tests/test_segmentation.pyc b/tippy/tests/test_segmentation.pyc new file mode 100644 index 0000000..782406d Binary files /dev/null and b/tippy/tests/test_segmentation.pyc differ diff --git a/tippy/tests/test_statistics.py b/tippy/tests/test_statistics.py index 803523f..19416f0 100644 --- a/tippy/tests/test_statistics.py +++ b/tippy/tests/test_statistics.py @@ -17,7 +17,8 @@ class Test(unittest.TestCase): This method is called before each test """ self.img_1c = cv.LoadImage("data/tippy.jpg", cv.CV_LOAD_IMAGE_GRAYSCALE) # 1 channel image - self.img_3c = cv.LoadImage("data/tippy.jpg", cv.CV_LOAD_IMAGE_COLOR) # 3 channel image + self.img_3c = cv.LoadImage("data/tippy.jpg", cv.CV_LOAD_IMAGE_COLOR) # 3 channel image + self.img_2c = cv.CreateImage((10, 10), cv.IPL_DEPTH_8U, 2) # fake 2 channels image self.img_16s = cv.CreateImage((15, 15), cv.IPL_DEPTH_16S, 1) # Non 8 bits image self.val = 10 # non Image @@ -33,48 +34,77 @@ class Test(unittest.TestCase): #--- ## TESTS def test_compute_histogram(self): - hist = st.Histogram() # testing input image - self.assertRaises(TypeError, lambda:hist.compute_histogram(self.val) ) - self.assertRaises(TypeError, lambda:hist.compute_histogram(self.img_3c)) - + self.assertRaises(TypeError, lambda:st.Histogram(self.val) ) + self.assertRaises(TypeError, lambda:st.Histogram(self.img_16s) ) + self.assertRaises(ValueError, lambda:st.Histogram(self.img_2c) ) # testing bin_fact self.assertRaises(ValueError, - lambda: hist.compute_histogram(self.img_1c, self.neg_val)) + lambda: st.Histogram(self.img_1c, self.neg_val)) self.assertRaises(TypeError, - lambda: hist.compute_histogram(self.img_1c, self.string)) + lambda: st.Histogram(self.img_1c, self.string)) # testing min_range. It can be either negative or even. self.assertRaises(TypeError, - lambda: hist.compute_histogram(self.img_1c, self.val, self.string)) + lambda: st.Histogram(self.img_1c, self.val, self.string)) - def test_hist2table(self): - hist = st.Histogram() - # testing input histogram - self.assertRaises(TypeError, lambda: hist.hist2table(self.val)) + # testing output + hist_1c = st.Histogram(self.img_1c) + self.assertEquals(self.img_1c.nChannels, hist_1c.channels) + hist_3c = st.Histogram(self.img_3c) + self.assertEquals(self.img_3c.nChannels, hist_3c.channels) + + def test_to_tables(self): # testing output size - hist.compute_histogram(self.img_1c) - histable = hist.hist2table() - self.assertEqual(256, len(histable)) + hist = st.Histogram(self.img_3c) + histable = hist.to_tables() + self.assertEqual(3, len(histable)) + self.assertEqual(256, len(histable[0])) - def test_hist2image(self): - hist = st.Histogram() - # testing input histogram - self.assertRaises(TypeError, lambda: hist.hist2image(self.val)) + def test_channel_to_image(self): + hist = st.Histogram(self.img_3c) + # testing channel value + self.assertRaises(ValueError, + lambda: hist.channel_to_image(4)) + self.assertRaises(TypeError, + lambda: hist.channel_to_image(self.string)) + self.assertRaises(TypeError, + lambda: hist.channel_to_image(self.neg_val)) # testing scale inputs self.assertRaises(TypeError, - lambda: hist.hist2image(hist, self.string)) + lambda: hist.channel_to_image(1, self.string)) self.assertRaises(TypeError, - lambda: hist.hist2image(hist, self.neg_val)) + lambda: hist.channel_to_image(1, self.neg_val)) self.assertRaises(TypeError, - lambda: hist.hist2image(hist, self.val, self.string)) + lambda: hist.channel_to_image(1, self.val, self.string)) self.assertRaises(TypeError, - lambda: hist.hist2image(hist, self.val, self.neg_val)) + lambda: hist.channel_to_image(1, self.val, self.neg_val)) # testing range inputs self.assertRaises(TypeError, - lambda: hist.hist2image(hist, self.val, self.val, self.string)) + lambda: hist.channel_to_image(1, self.val, self.val, self.string)) self.assertRaises(TypeError, - lambda: hist.hist2image(hist, self.val, self.val, self.neg_val)) + lambda: hist.channel_to_image(1, self.val, self.val, self.neg_val)) + + def test_to_images(self): + hist = st.Histogram(self.img_3c) + # testing scale inputs + self.assertRaises(TypeError, + lambda: hist.to_images(self.string)) + self.assertRaises(TypeError, + lambda: hist.to_images(self.neg_val)) + self.assertRaises(TypeError, + lambda: hist.to_images(self.val, self.string)) + self.assertRaises(TypeError, + lambda: hist.to_images(self.val, self.neg_val)) + # testing range inputs + self.assertRaises(TypeError, + lambda: hist.to_images(self.val, self.val, self.string)) + self.assertRaises(TypeError, + lambda: hist.to_images(self.val, self.val, self.neg_val)) + + # testing outputs + self.assertEqual(hist.channels, len(hist.to_images())) + #if __name__ == "__main__": #import sys;sys.argv = ['', 'Test.testName'] #unittest.main() \ No newline at end of file diff --git a/tippy/tests/test_statistics.pyc b/tippy/tests/test_statistics.pyc new file mode 100644 index 0000000..844aa01 Binary files /dev/null and b/tippy/tests/test_statistics.pyc differ