From 19b5973c44c13f9b411afe653e89ee87a6e5ce5e Mon Sep 17 00:00:00 2001 From: sccolbert Date: Wed, 2 Dec 2009 20:11:49 +0100 Subject: [PATCH 1/3] worked on prettying up scivi. --- scikits/image/io/_plugins/_colormixer.pyx | 1 + scikits/image/io/_plugins/q_color_mixer.py | 5 +++-- scikits/image/io/_plugins/q_histogram.py | 9 ++++++--- scikits/image/io/_plugins/scivi.py | 17 +++++++++++++---- 4 files changed, 23 insertions(+), 9 deletions(-) diff --git a/scikits/image/io/_plugins/_colormixer.pyx b/scikits/image/io/_plugins/_colormixer.pyx index 3bf30d53..5d0dd1b9 100644 --- a/scikits/image/io/_plugins/_colormixer.pyx +++ b/scikits/image/io/_plugins/_colormixer.pyx @@ -248,6 +248,7 @@ cdef void rgb_2_hsv(float* RGB, float* HSV) nogil: else: pass + if R < G: MIN = R MAX = G diff --git a/scikits/image/io/_plugins/q_color_mixer.py b/scikits/image/io/_plugins/q_color_mixer.py index 4c37cc4f..5a60b92b 100644 --- a/scikits/image/io/_plugins/q_color_mixer.py +++ b/scikits/image/io/_plugins/q_color_mixer.py @@ -66,7 +66,7 @@ class IntelligentSlider(QWidget): return self.slider.value() * self.a + self.b -class MixerPanel(QWidget): +class MixerPanel(QtGui.QFrame): '''A color mixer to hook up to an image. You pass the image you the panel to operate on and it operates on that image in place. You also @@ -74,7 +74,8 @@ class MixerPanel(QWidget): This callback is called every time the mixer modifies your image.''' def __init__(self, img): - QWidget.__init__(self) + QtGui.QFrame.__init__(self) + #self.setFrameStyle(QtGui.QFrame.Box|QtGui.QFrame.Sunken) self.img = img self.mixer = ColorMixer(self.img) diff --git a/scikits/image/io/_plugins/q_histogram.py b/scikits/image/io/_plugins/q_histogram.py index cfdc889f..36edb30d 100644 --- a/scikits/image/io/_plugins/q_histogram.py +++ b/scikits/image/io/_plugins/q_histogram.py @@ -1,6 +1,6 @@ import numpy as np -from PyQt4.QtGui import QWidget, QPainter, QGridLayout, QColor +from PyQt4.QtGui import QWidget, QPainter, QGridLayout, QColor, QFrame from util import histograms @@ -103,7 +103,7 @@ class ColorHistogram(QWidget): self.repaint() -class QuadHistogram(QWidget): +class QuadHistogram(QFrame): '''A class which uses ColorHistogram to draw the 4 histograms of an image. R, G, B, and Value. @@ -113,14 +113,17 @@ class QuadHistogram(QWidget): ''' def __init__(self, img, layout='vertical', order=['R', 'G', 'B', 'V']): - QWidget.__init__(self) + QFrame.__init__(self) + r, g, b, v = histograms(img, 100) self.r_hist = ColorHistogram(r, (255, 0, 0)) self.g_hist = ColorHistogram(g, (0, 255, 0)) self.b_hist = ColorHistogram(b, (0, 0, 255)) self.v_hist = ColorHistogram(v, (0, 0, 0)) + self.setFrameStyle(QFrame.StyledPanel|QFrame.Sunken) self.layout = QGridLayout(self) + self.layout.setMargin(0) order_map = {'R': self.r_hist, 'G': self.g_hist, 'B': self.b_hist, 'V': self.v_hist} diff --git a/scikits/image/io/_plugins/scivi.py b/scikits/image/io/_plugins/scivi.py index c6197717..3c2c0de9 100644 --- a/scikits/image/io/_plugins/scivi.py +++ b/scikits/image/io/_plugins/scivi.py @@ -66,9 +66,11 @@ class ImageLabel(QLabel): self.setPixmap(pm) -class RGBHSVDisplay(QWidget): +class RGBHSVDisplay(QFrame): def __init__(self): - QWidget.__init__(self) + QFrame.__init__(self) + self.setFrameStyle(QtGui.QFrame.Box|QtGui.QFrame.Sunken) + self.posx_label = QLabel('X-pos:') self.posx_value = QLabel() self.posy_label = QLabel('Y-pos:') @@ -129,8 +131,15 @@ class SciviImageWindow(QMainWindow): self.setCentralWidget(self.main_widget) self.label = ImageLabel(self, arr) - self.layout.addWidget(self.label, 0, 0) - self.layout.addLayout + self.label_container = QFrame() + self.label_container.setFrameShape(QtGui.QFrame.StyledPanel|QtGui.QFrame.Sunken) + self.label_container.setLineWidth(1) + + self.label_container.layout = QtGui.QGridLayout(self.label_container) + self.label_container.layout.setMargin(0) + self.label_container.layout.addWidget(self.label, 0, 0) + self.layout.addWidget(self.label_container, 0, 0) + self.mgr.add_window(self) self.main_widget.show() From 1b53c9d01f4312c6e9a6c1c1e9c18a59ecf1fdce Mon Sep 17 00:00:00 2001 From: sccolbert Date: Sat, 5 Dec 2009 18:29:27 +0100 Subject: [PATCH 2/3] added cvFindExtrinsicCameraParams2 and cvWatershed --- scikits/image/opencv/opencv_cv.pyx | 157 +++++++++++++++++++++++++++++ 1 file changed, 157 insertions(+) diff --git a/scikits/image/opencv/opencv_cv.pyx b/scikits/image/opencv/opencv_cv.pyx index 87a96f59..4c634993 100644 --- a/scikits/image/opencv/opencv_cv.pyx +++ b/scikits/image/opencv/opencv_cv.pyx @@ -259,6 +259,11 @@ ctypedef void (*cvPyrUpPtr)(IplImage*, IplImage*, int) cdef cvPyrUpPtr c_cvPyrUp c_cvPyrUp = (ctypes.addressof(cv.cvPyrUp))[0] +# cvWatershed +ctypedef void (*cvWatershedPtr)(IplImage*, IplImage*) +cdef cvWatershedPtr c_cvWatershed +c_cvWatershed = (ctypes.addressof(cv.cvWatershed))[0] + # cvCalibrateCamera2 ctypedef void (*cvCalibrateCamera2Ptr)(CvMat*, CvMat*, CvMat*, CvSize, CvMat*, CvMat*, CvMat*, CvMat*, int) @@ -278,6 +283,14 @@ cdef cvFindChessboardCornersPtr c_cvFindChessboardCorners c_cvFindChessboardCorners = ( ctypes.addressof(cv.cvFindChessboardCorners))[0] +# cvFindExtrinsicCameraParams2 +ctypedef void (*cvFindExtrinsicCameraParams2Ptr)(CvMat*, CvMat*, CvMat*, CvMat*, + CvMat*, CvMat*, int) +cdef cvFindExtrinsicCameraParams2Ptr c_cvFindExtrinsicCameraParams2 +c_cvFindExtrinsicCameraParams2 = \ + ( + ctypes.addressof(cv.cvFindExtrinsicCameraParams2))[0] + # cvDrawChessboardCorners ctypedef void (*cvDrawChessboardCornersPtr)(IplImage*, CvSize, CvPoint2D32f*, int, int) @@ -2068,6 +2081,54 @@ def cvPyrUp(np.ndarray src): return out +#------------ +# cvWatershed +#------------ + +@cvdoc(package='cv', group='image', doc=\ +'''cvWatershed(src, markers) + +Performs watershed segmentation. + +Parameters +---------- +src : ndarray, 3D, dtype=uint8 + The source image. +markers : ndarray, 2D, dtype=int32 + The markers identifying the regions of interest. + Marker values should be non-zero. + This array should have the same width and height as src. + +Returns +------- +None : None + The markers array is modified in place. The results of which + identify the segmented regions of the image.''') +def cvWatershed(src, markers): + + validate_array(src) + validate_array(markers) + + assert_ndims(src, [3]) + assert_dtype(src, [UINT8]) + + assert_ndims(markers, [2]) + assert_dtype(markers, [INT32]) + + #assert src.shape[:2] == markers.shape[:2], \ + # 'The src and markers array must have same width and height' + + cdef IplImage srcimg + cdef IplImage markersimg + + populate_iplimage(src, &srcimg) + populate_iplimage(markers, &markersimg) + + c_cvWatershed(&srcimg, &markersimg) + + return None + + #------------------- # cvCalibrateCamera2 #------------------- @@ -2279,6 +2340,102 @@ def cvFindChessboardCorners(np.ndarray src, pattern_size, return out[:ncorners_found] +#----------------------------- +# cvFindExtrinsicCameraParams2 +#----------------------------- + +@cvdoc(package='cv', group='calibration', doc=\ +'''cvFindExtrinsicCameraParams2(object_points, image_points, intrinsic_matrix, + distortion_coeffs) + +Calculates the extrinsic camera parameters given a set of 3D points, their +2D locations in the image, and the camera instrinsics matrix and distortion +coefficients. + +i.e. given this information, it calculates the offset and rotation of the +camera from the chessboard origin. + +Parameters +---------- +object_points: ndarray, nx3 + The 3D coordinates of the chessboard corners. +image_points: ndarray, nx2 + The 2D image coordinates of the object_points +intrinsic_matrix: ndarray, 3x3, dtype=float64 + The 2D camera intrinsics matrix that is the result of camera calibration +distortion_coeffs: ndarray, 5-vector, dtype=float64 + The 5 distortion coefficients that are the result of camera calibration + +Returns +------- +(rvec, tvec): ndarray 3-vector dtype=float64, ndarray 3-vector dtype=float64 + rvec - the rotation vector representing the rotation of the camera + relative to the chessboard. The direction of the vector represents the + axis of rotation and its magnitude the amount of rotation. + tvec - the translation vector representing the offset of the camera + relative to the chessboard origin.''') +def cvFindExtrinsicCameraParams2(object_points, image_points, intrinsic_matrix, + distortion_coeffs): + + validate_array(object_points) + validate_array(image_points) + validate_array(intrinsic_matrix) + + assert_ndims(object_points, [2]) + assert_dtype(object_points, [FLOAT32, FLOAT64]) + assert object_points.shape[1] == 3, 'object_points should be nx3' + + assert_ndims(image_points, [2]) + assert_dtype(image_points, [FLOAT32, FLOAT64]) + assert image_points.shape[1] == 2, 'image_points should be nx2' + + assert_dtype(intrinsic_matrix, [FLOAT64]) + assert intrinsic_matrix.shape == (3, 3), 'instrinsics should be 3x3' + + assert_dtype(distortion_coeffs, [FLOAT64]) + assert distortion_coeffs.shape == (5,), 'distortions should be 5-vector' + + # allocate the numpy return arrays + cdef np.npy_intp shape[1] + shape[0] = 3 + cdef np.ndarray rvec = new_array(1, shape, FLOAT64) + cdef np.ndarray tvec = new_array(1, shape, FLOAT64) + + # allocate the cv images + cdef IplImage obj_img + cdef IplImage img_img + cdef IplImage intr_img + cdef IplImage dist_img + cdef IplImage rot_img + cdef IplImage tran_img + populate_iplimage(object_points, &obj_img) + populate_iplimage(image_points, &img_img) + populate_iplimage(intrinsic_matrix, &intr_img) + populate_iplimage(distortion_coeffs, &dist_img) + populate_iplimage(rvec, &rot_img) + populate_iplimage(tvec, &tran_img) + + # allocate the cv mats + cdef CvMat* cvobj = cvmat_ptr_from_iplimage(&obj_img) + cdef CvMat* cvimg = cvmat_ptr_from_iplimage(&img_img) + cdef CvMat* cvint = cvmat_ptr_from_iplimage(&intr_img) + cdef CvMat* cvdis = cvmat_ptr_from_iplimage(&dist_img) + cdef CvMat* cvrot = cvmat_ptr_from_iplimage(&rot_img) + cdef CvMat* cvtrn = cvmat_ptr_from_iplimage(&tran_img) + + # the last argument is new to OpenCV 2.0 and tells it NOT to use + # an extrinsics guess + c_cvFindExtrinsicCameraParams2(cvobj, cvimg, cvint, cvdis, cvrot, cvtrn, 0) + + PyMem_Free(cvobj) + PyMem_Free(cvimg) + PyMem_Free(cvint) + PyMem_Free(cvdis) + PyMem_Free(cvrot) + PyMem_Free(cvtrn) + + return (rvec, tvec) + #------------------------ # cvFindChessboardCorners #------------------------ From ae4bd3449547b4dad0baf00d6c33fd20cd047166 Mon Sep 17 00:00:00 2001 From: sccolbert Date: Sun, 6 Dec 2009 22:30:22 +0100 Subject: [PATCH 3/3] added cvFloodFill and cvMatchTemplate --- scikits/image/opencv/opencv_constants.py | 10 + scikits/image/opencv/opencv_cv.pyx | 251 +++++++++++++++++++++++ 2 files changed, 261 insertions(+) diff --git a/scikits/image/opencv/opencv_constants.py b/scikits/image/opencv/opencv_constants.py index 76b97b24..a7bec83d 100644 --- a/scikits/image/opencv/opencv_constants.py +++ b/scikits/image/opencv/opencv_constants.py @@ -201,5 +201,15 @@ CV_64FC2 = _CV_MAKETYPE(CV_64F,2) CV_64FC3 = _CV_MAKETYPE(CV_64F,3) CV_64FC4 = _CV_MAKETYPE(CV_64F,4) +#------------------------------------------------------------------------------- +# Template Matching +#------------------------------------------------------------------------------- +CV_TM_SQDIFF = 0 +CV_TM_SQDIFF_NORMED = 1 +CV_TM_CCORR = 2 +CV_TM_CCORR_NORMED = 3 +CV_TM_CCOEFF = 4 +CV_TM_CCOEFF_NORMED = 5 + diff --git a/scikits/image/opencv/opencv_cv.pyx b/scikits/image/opencv/opencv_cv.pyx index 4c634993..82bf54a8 100644 --- a/scikits/image/opencv/opencv_cv.pyx +++ b/scikits/image/opencv/opencv_cv.pyx @@ -298,6 +298,18 @@ cdef cvDrawChessboardCornersPtr c_cvDrawChessboardCorners c_cvDrawChessboardCorners = ( ctypes.addressof(cv.cvDrawChessboardCorners))[0] +# cvFloodFill +ctypedef void (*cvFloodFillPtr)(IplImage*, CvPoint, CvScalar, CvScalar, + CvScalar, void*, int, IplImage*) +cdef cvFloodFillPtr c_cvFloodFill +c_cvFloodFill = (ctypes.addressof(cv.cvFloodFill))[0] + +# cvMatchTemplate +ctypedef void (*cvMatchTemplatePtr)(IplImage*, IplImage*, IplImage*, int) +cdef cvMatchTemplatePtr c_cvMatchTemplate +c_cvMatchTemplate = ( + ctypes.addressof(cv.cvMatchTemplate))[0] + #------------------------------------------------------------------------------- # Function Implementations #------------------------------------------------------------------------------- @@ -2509,3 +2521,242 @@ def cvDrawChessboardCorners(np.ndarray src, pattern_size, np.ndarray corners, return out +#------------ +# cvFloodFill +#------------ + +@cvdoc(package='cv', group='image', doc=\ +'''cvFloodFill(np.ndarray src, seed_point, new_val, low_diff, high_diff, + mask=None, connect_diag=False, mask_only=False, + mask_fillval=None, fixed_range=False) + +Fills a connected component with the given color. + +Parameters +---------- +src : ndarray, ndims=[2, 3], dtypes[uint8, float32] + The source image +seed_point : (x, y) int tuple + The starting point of the fill in image pixel coordinates. +new_val : scalar double or 3-tuple (R, G, B) doubles + The color value of the repainted area. If a scalar, the RGB values + are all set equal to the scalar. +low_diff : scalar double or 3-tuple (R, G, B) doubles + Maximal lower brightness/color difference between the currently + observed pixel and one of its neighbors belonging to the component, + or a seed pixel being added to the component. Must be positive. +high_diff : scalar double or 3-tuple (R, G, B) doubles + Maximal upper brightness/color difference between the currently + observed pixel and one of its neighbors belonging to the component, + or a seed pixel being added to the component. Must be positive. +mask : ndarray 2d, dtype=uint8 or None + The mask in which to draw the results and/or use as a mask. + See the opencv documentation for more details. + If not None, the mask shape must be 2 pixels wider and taller than src. +connect_diag : bool + If True, implies connectivity across the diagonals in addition to + the standard horizontal and vertical directions. +mask_only : bool + If True, fill the mask instead of the image. + Mask must not be None +mask_fillval : int 0 - 255 or None + The value to fill the mask if mask is not None. + If None, defaults to 1 +fixed_range : bool + If True, fills relative to seed value, else, fills relative to + neighbors value. + +Returns +------- +None : None + This is an in-place operation which draws into src and/or image depending + on the flags set in the input arguments''') +def cvFloodFill(np.ndarray src, seed_point, new_val, low_diff, high_diff, + mask=None, connect_diag=False, mask_only=False, + mask_fillval=None, fixed_range=False): + + validate_array(src) + assert_ndims(src, [2, 3]) + assert_dtype(src, [UINT8, FLOAT32]) + + # src + cdef IplImage srcimg + populate_iplimage(src, &srcimg) + + # seed_point + if len(seed_point) != 2: + raise ValueError('seed_point should be an (x, y) tuple of ints') + cdef CvPoint cv_seed_point + cdef int x = seed_point[0] + cdef int y = seed_point[1] + cdef int xmax = src.shape[1] + cdef int ymax = src.shape[0] + if x < 0 or x > xmax or y < 0 or y > ymax: + raise ValueError('seed_point must be image pixel coordinates') + cv_seed_point.x = x + cv_seed_point.y = y + + # loop counter + cdef int i + cdef double temp + + # new_val + cdef CvScalar cv_new_val + if hasattr(new_val, '__len__'): + if len(new_val) != 3: + raise ValueError('If not a scalar, new_val must be 3 tuple') + for i in range(3): + cv_new_val.val[i] = new_val[i] + else: + temp = new_val + for i in range(3): + cv_new_val.val[i] = temp + + # low_diff + cdef CvScalar cv_low_diff + if hasattr(low_diff, '__len__'): + if len(low_diff) != 3: + raise ValueError('If not a scalar, low_diff must be 3 tuple') + for i in range(3): + cv_low_diff.val[i] = low_diff[i] + else: + temp = low_diff + for i in range(3): + cv_low_diff.val[i] = temp + + # high_diff + cdef CvScalar cv_high_diff + if hasattr(high_diff, '__len__'): + if len(high_diff) != 3: + raise ValueError('If not a scalar, high_diff must be 3 tuple') + for i in range(3): + cv_high_diff.val[i] = high_diff[i] + else: + temp = high_diff + for i in range(3): + cv_high_diff.val[i] = temp + + # mask + cdef IplImage maskimg + cdef IplImage* maskimgptr = NULL + if mask is not None: + validate_array(mask) + assert_ndims(mask, [2]) + assert_dtype(mask, [UINT8]) + if mask.shape[0] != (src.shape[0] + 2) or \ + mask.shape[1] != (src.shape[1] + 2): + raise ValueError('mask must be 2 pixels wider and taller than src.') + populate_iplimage(mask, &maskimg) + maskimgptr = &maskimg + + # flags + cdef int flags + + # connect_diag + cdef int cv_connect_diag = 4 + if connect_diag: + cv_connect_diag = 8 + + # mask_only + cdef int cv_mask_only = 0 + if mask_only: + if mask is None: + raise ValueError('If mask_only==True, mask must not be None') + cv_mask_only = (1 << 17) + + # mask_fillval + cdef int cv_mask_fillval = (1 << 8) + if mask_fillval: + if mask_fillval < 0 or mask_fillval > 255: + raise ValueError('mask_fillval must be in range 0-255') + cv_mask_fillval = ((mask_fillval) << 8) + + # fixed_range + cdef int cv_fixed_range = 0 + if fixed_range: + cv_fixed_range = (1 << 16) + + flags = cv_connect_diag | cv_mask_only | cv_mask_fillval | cv_fixed_range + + c_cvFloodFill(&srcimg, cv_seed_point, cv_new_val, cv_low_diff, cv_high_diff, + NULL, flags, maskimgptr) + + return None + + +#---------------- +# cvMatchTemplate +#---------------- + +@cvdoc(package='cv', group='image', doc=\ +'''cvMatchTemplate(src, template, method) + +Compares a template against overlapped image regions and returns a match array +dependent on the match method requested. + +Parameters +---------- +src : ndarray, ndims=[2, 3], dtype=[uint8, float32] + The source image. +template : ndarray, ndim=src.ndim, dtype=src.dtype + The template to match in the source. +method : int + The method to use for matching. + One of: + CV_TM_SQDIFF + CV_TM_SQDIFF_NORMED + CV_TM_CCORR + CV_TM_CCORR_NORMED + CV_TM_CCOEFF + CV_TM_CCOEFF_NORMED + +Returns +------- +out : ndarray, 2d, dtype=float3d + The results of the template matching. + The size of this array (H - h + 1) x (W - w + 1) + where (H, W) is (Height, Width) of src and (h, w) is + (height, width) of template. + +Notes +----- +After the function finishes the comparison, the best matches can be found +as global minimums (CV_TM_SQDIFF) or maximums (CV_TM_CCORR and CV_TM_CCOEFF) +using the appropriate numpy functions. In the case of a color image, +template summation in the numerator and each sum in the denominator +is done over all of the channels (and separate mean values are used for each +channel).''') +def cvMatchTemplate(np.ndarray src, np.ndarray template, int method): + + validate_array(src) + validate_array(template) + + assert_ndims(src, [2, 3]) + assert_dtype(src, [UINT8, FLOAT32]) + + assert_ndims(template, [src.ndim]) + assert_dtype(template, [src.dtype]) + + if method not in [CV_TM_SQDIFF_NORMED, CV_TM_CCORR, CV_TM_CCORR_NORMED, + CV_TM_CCOEFF, CV_TM_CCOEFF_NORMED]: + raise ValueError('Unknown method type') + + if src.shape[0] <= template.shape[0] or src.shape[1] <= template.shape[1]: + raise ValueError('template must be smaller than source image') + + cdef np.npy_intp outshape[2] + outshape[0] = (src.shape[0] - template.shape[0] + 1) + outshape[1] = (src.shape[1] - template.shape[1] + 1) + cdef np.ndarray out = new_array(2, outshape, FLOAT32) + + cdef IplImage srcimg + cdef IplImage templateimg + cdef IplImage outimg + + populate_iplimage(src, &srcimg) + populate_iplimage(template, &templateimg) + populate_iplimage(out, &outimg) + + c_cvMatchTemplate(&srcimg, &templateimg, &outimg, method) + + return out