From f6d9b0b047337a732f72b9839086c3e1b371d7d2 Mon Sep 17 00:00:00 2001 From: Julien Lengrand-Lambert Date: Sun, 27 Nov 2011 23:05:41 +0100 Subject: [PATCH] Adds mouse_point function to library Implements mouse_pos function Implements corresponding tests Two modes included, single and multiple. TODO: How to tests functions needing user inputs? Signed-off-by: Julien Lengrand-Lambert --- tippy/basic_operations.py | 75 +++++++++++++++++++++++++++- tippy/segmentations.py | 7 ++- tippy/tests/test_basic_operations.py | 15 ++++++ tippy/tests/test_segmentation.py | 2 + 4 files changed, 97 insertions(+), 2 deletions(-) diff --git a/tippy/basic_operations.py b/tippy/basic_operations.py index 91b4a4b..1319548 100644 --- a/tippy/basic_operations.py +++ b/tippy/basic_operations.py @@ -11,6 +11,77 @@ Created on Nov 19, 2011 import sys import cv +_mouse_pos = [] + +def _on_mouse(event, x, y, flags, param): + """ + None = onMouse(event, x, y, flags, param) + + Function automatically called by opencv when having mouse events on a + displayed frame. In here, we are searching for a left mouse click + """ + global _mouse_pos + if event == cv.CV_EVENT_LBUTTONDOWN : + _mouse_pos.append((x, y)) + +def mouse_point(img, name="Mouse Points", mode="S"): + """ + Displays input image and waits for user click on a point of the image. + For each click, the (x, y) coordinates are retrieved. + + Two modes are possible: + -Single : + The function exits automatically on first click. + Returns a list containing 1 (x,y) tuple. + -Multiple : + All clicks are saved. The function exists when the user types on keyboard + Returns containing (x,y) tuples. + --- + Ex: + img = cv.LoadImage("../data/tippy.jpg", cv.CV_LOAD_IMAGE_GRAYSCALE) + mouse_points(img, name="mouse points", mode="S") + """ + + if not isinstance(name, str): + raise TypeError("(%s) Name :String expected!" % (sys._getframe().f_code.co_name)) + if not isinstance(mode, str): + raise TypeError("(%s) Mode :String expected!" % (sys._getframe().f_code.co_name)) + + global _mouse_pos + cv.StartWindowThread() + cv.NamedWindow(name, cv.CV_WINDOW_AUTOSIZE) + cv.SetMouseCallback(name, _on_mouse) + + try : + cv.ShowImage(name, img) + except TypeError: + raise TypeError("(%s) img : IplImage expected!" % (sys._getframe().f_code.co_name)) + + if mode == "S": + _single_point() + elif mode == "M": + _multiple_points() + else: + raise ValueError("(%s) Mode can be either S (single) or M (multiple)" % (sys._getframe().f_code.co_name)) + + cv.DestroyWindow(name) + return _mouse_pos + +def _single_point(): + """ + Internal function, used in mouse_point + """ + while(len(_mouse_pos) == 0): + cv.WaitKey(10) + +def _multiple_points(): + """ + Internal function, used in mouse_point + """ + char = -1 + while (char == -1): + char = cv.WaitKey(10) +# ---- def create_same_image(in_img, zero=0): """ Creates an image of same size, depth and number of channels as input image @@ -31,4 +102,6 @@ def create_same_image(in_img, zero=0): if zero: cv.Zero(out_img) - return out_img \ No newline at end of file + return out_img + + diff --git a/tippy/segmentations.py b/tippy/segmentations.py index f15c3db..15e825a 100644 --- a/tippy/segmentations.py +++ b/tippy/segmentations.py @@ -20,6 +20,11 @@ def simple_region_growing(img, seed, threshold=1): Outputs a single channel 8 bits binary (0 or 255) image. Extracted region is highlighted in white. """ + try: + dims = cv.GetSize(img) + except TypeError: + raise TypeError("(%s) img : IplImage expected!" % (sys._getframe().f_code.co_name)) + # img test if not(img.depth == cv.IPL_DEPTH_8U): raise TypeError("(%s) 8U image expected!" % (sys._getframe().f_code.co_name)) @@ -34,7 +39,7 @@ def simple_region_growing(img, seed, threshold=1): if not((isinstance(seed, tuple)) and (len(seed) is 2) ) : raise TypeError("(%s) (x, y) variable expected!" % (sys._getframe().f_code.co_name)) - dims = cv.GetSize(img) + if (seed[0] or seed[1] ) < 0 : raise ValueError("(%s) Seed should have positive values!" % (sys._getframe().f_code.co_name)) elif ((seed[0] > dims[0]) or (seed[1] > dims[1])): diff --git a/tippy/tests/test_basic_operations.py b/tippy/tests/test_basic_operations.py index 07aa235..c942939 100644 --- a/tippy/tests/test_basic_operations.py +++ b/tippy/tests/test_basic_operations.py @@ -24,6 +24,11 @@ class Test(unittest.TestCase): self.img_3c = cv.LoadImage("../data/tippy.jpg", cv.CV_LOAD_IMAGE_COLOR) # 3 channel image self.img_fake_2c = cv.CreateImage((15, 15), cv.IPL_DEPTH_8U, 2) # 2 channels self.fake_img = 10 # non Image + + self.false_mode = "A" # non True/False (or 0/1) argument + self.fake_mode = 100 # non True/False (or 0/1) argument + self.fake_name = 100 # non True/False (or 0/1) argument + def tearDown(self): """ This method is called after each test @@ -40,5 +45,15 @@ class Test(unittest.TestCase): self.assertEqual(cv.GetSize(img), cv.GetSize(out_img)) self.assertRaises(TypeError, lambda: bo.create_same_image(self.fake_img)) + def test_mouse_point(self): + # img test + self.assertRaises(TypeError, lambda: bo.mouse_point(self.fake_img)) + # name test + self.assertRaises(TypeError, lambda: bo.mouse_point(self.fake_name)) + # mode test + self.assertRaises(TypeError, lambda: bo.mouse_point(self.fake_mode)) + self.assertRaises(ValueError, lambda: bo.mouse_point(self.false_mode)) + # TODO: How to test arguments that need user interaction? + #if __name__ == "__main__": #unittest.main() \ No newline at end of file diff --git a/tippy/tests/test_segmentation.py b/tippy/tests/test_segmentation.py index 550c64a..0082cf1 100644 --- a/tippy/tests/test_segmentation.py +++ b/tippy/tests/test_segmentation.py @@ -44,6 +44,8 @@ class Test(unittest.TestCase): # Image type tests self.assertRaises(TypeError, lambda: se.simple_region_growing(self.img_3c, self.seed)) self.assertRaises(TypeError, lambda: se.simple_region_growing(self.img_16s, self.seed)) + self.assertRaises(TypeError, lambda: se.simple_region_growing(self.fake_img, self.seed)) + # Threshold tests self.assertRaises(ValueError, lambda: se.simple_region_growing(self.img_1c, self.seed, self.neg_thres)) self.assertRaises(TypeError, lambda: se.simple_region_growing(self.img_1c, self.seed, self.bad_thres))