diff --git a/examples/histogram_display.py b/examples/histogram_display.py index df4f48a..ef1a6d5 100644 --- a/examples/histogram_display.py +++ b/examples/histogram_display.py @@ -23,12 +23,10 @@ import cv import tippy.statistics as st import tippy.display_operations as do -hist = st.Histogram() - img_name = "tippy/data/gnu.jpg" -img = cv.LoadImage(img_name, cv.CV_LOAD_IMAGE_GRAYSCALE) -hist.compute_histogram(img) +img = cv.LoadImage(img_name, cv.CV_LOAD_IMAGE_COLOR) +hist = st.Histogram(img) hist_img = hist.hist2image() -do.display_single_image(hist_img, "Gray-Level histogram of the gnu image") +do.display_single_image(hist_img[0], "Gray-Level histogram of the gnu image") diff --git a/tippy/statistics.py b/tippy/statistics.py index 19d6584..b5f8fae 100644 --- a/tippy/statistics.py +++ b/tippy/statistics.py @@ -18,12 +18,13 @@ class Histogram(): NOTE : Supports only 1 channel / 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): """ @@ -40,7 +41,7 @@ class Histogram(): """ # 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,15 +71,41 @@ 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 + 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): """ @@ -94,9 +120,11 @@ class Histogram(): % (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 hist2image(self, scale_x=3, scale_y=3, y_range=64): diff --git a/tippy/tests/test_statistics.py b/tippy/tests/test_statistics.py index 803523f..f7f9c5e 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,34 +34,35 @@ 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)) + + # 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_hist2table(self): - hist = st.Histogram() - # testing input histogram - self.assertRaises(TypeError, lambda: hist.hist2table(self.val)) # testing output size - hist.compute_histogram(self.img_1c) + hist = st.Histogram(self.img_3c) histable = hist.hist2table() - self.assertEqual(256, len(histable)) + 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_hist2image(self): + hist = st.Histogram(self.img_1c) # testing scale inputs self.assertRaises(TypeError, lambda: hist.hist2image(hist, self.string))