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
|
#
#
# Copyright (C) 2001-2005 Ichiro Fujinaga, Michael Droettboom, Karl MacMillan
# 2008-2012 Christoph Dalitz
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
"""The image utilities module contains plugins that do not fit in
any other category, like image copying or computing histograms."""
from gamera.plugin import *
from gamera.gui import has_gui
from gamera.util import warn_deprecated
from gamera.args import NoneDefault
import sys
import _image_utilities
class image_copy(PluginFunction):
"""
Copies an image along with all of its underlying data. Since the data is
copied, changes to the new image do not affect the original image.
*storage_format*
specifies the compression type for the returned copy:
DENSE (0)
no compression
RLE (1)
run-length encoding compression
"""
category = "Utility"
self_type = ImageType(ALL)
return_type = ImageType(ALL)
args = Args([Choice("storage_format", ["DENSE", "RLE"])])
def __call__(image, storage_format = 0):
if image.nrows <= 0 or image.ncols <= 0:
return image
return _image_utilities.image_copy(image, storage_format)
__call__ = staticmethod(__call__)
class image_save(PluginFunction):
"""
Saves an image to file with specified name and format.
"""
pure_python = 1
category = "Utility"
self_type = ImageType(ALL)
args = Args([FileSave("image_file_name", "", "*"),
Choice("File format", ["TIFF", "PNG"])
])
def __call__(image, name, format):
if format == 0 or str(format).upper() == "TIFF":
try:
from gamera.plugins import tiff_support
except ImportError:
raise ImportError("Could not load TIFF support.")
image.save_tiff(name)
elif format == 1 or str(format).upper() == "PNG":
try:
from gamera.plugins import png_support
except ImportError:
raise ImportError("Could not load PNG support.")
image.save_PNG(name)
__call__ = staticmethod(__call__)
class histogram(PluginFunction):
"""
Compute the histogram of the pixel values in the given image.
Returns a Python array of doubles, with each value being a
percentage.
If the GUI is being used, the histogram is displayed.
.. image:: images/histogram.png
"""
category = "Analysis"
self_type = ImageType([GREYSCALE, GREY16])
return_type = FloatVector()
doc_examples = [(GREYSCALE,)]
def __call__(image):
hist = _image_utilities.histogram(image)
if has_gui.has_gui == has_gui.WX_GUI:
has_gui.gui.ShowHistogram(hist, mark=image.otsu_find_threshold())
return hist
__call__ = staticmethod(__call__)
class union_images(PluginFunction):
"""
Returns an image that is the union of the given list of connected
components.
"""
category = "Combine"
self_type = None
args = Args([ImageList('list_of_images')])
return_type = ImageType([ONEBIT])
class fill_white(PluginFunction):
"""
Fills the entire image with white.
"""
category = "Draw"
self_type = ImageType([ONEBIT, GREYSCALE, GREY16, FLOAT, RGB])
class fill(PluginFunction):
"""
Fills the entire image with value.
*value*
A pixel value. This value may be any value the pixel type can support.
"""
category = "Draw"
self_type = ImageType(ALL)
args = Args([Pixel("value")])
class pad_image_default(PluginFunction):
"""Pads an image with the default pixel value white"""
# This is only for plugin generation, it will not be added to the image type
# (since self_type == None)
category = None
self_type = None
args = Args([ImageType(ALL), Int("top"), Int("right"), Int("bottom"), Int("left")])
return_type = ImageType(ALL)
class pad_image(PluginFunction):
"""
Pads an image with any value. When no pixel value is given, the value
corresponding to the color *white* is used.
*top*
Padding on the top.
*right*
Padding on the right.
*bottom*
Padding on the bottom.
*left*
Padding on the left.
*value*
An optional pixel value of the pixel type of the image.
When omitted or set to ``None``, the color white is used for padding.
"""
category = "Utility"
self_type = ImageType(ALL)
args = Args([Int("top"), Int("right"), Int("bottom"), Int("left"), Pixel("value")])
return_type = ImageType(ALL)
_pad_image_default = pad_image_default()
def __call__(self, top, right, bottom, left, value=None):
if value is None:
return pad_image._pad_image_default(self, top, right, bottom, left)
else:
return _image_utilities.pad_image(self, top, right, bottom, left, value)
__call__ = staticmethod(__call__)
doc_examples = [(RGB, 5, 10, 15, 20)]
class trim_image(PluginFunction):
"""
Returns minimal view so that outside of the view only Pixels with
*PixelValue* exists. When no *PixelValue* is given, white is used.
"""
category = "Utility"
self_type = ImageType([ONEBIT, GREYSCALE, GREY16, FLOAT, RGB])
args = Args([Pixel("PixelValue", default=NoneDefault)])
return_type = ImageType([ONEBIT, GREYSCALE, GREY16, FLOAT, RGB])
author = "Tobias Bolten"
def __call__(self, PixelValue=None):
if (PixelValue == None):
PixelValue = self.white()
return _image_utilities.trim_image(self, PixelValue)
__call__ = staticmethod(__call__)
class invert(PluginFunction):
"""
Inverts the image.
"""
category = "Draw"
self_type = ImageType([ONEBIT, GREYSCALE, GREY16, RGB])
doc_examples = [(RGB,), (GREYSCALE,), (ONEBIT,)]
class clip_image(PluginFunction):
"""
Crops an image so that the bounding box includes only the
intersection of it and another image. Returns a zero-sized image
if the two images do not intersect.
"""
category = "Utility"
return_type = ImageType(ALL)
self_type = ImageType(ALL)
args = Args(Rect("other"))
class mask(PluginFunction):
"""
Masks an image using the given ONEBIT image. Parts of the ONEBIT
image that are white will be changed to white in the resulting
image.
The images must be the same size.
"""
category = "Combine"
return_type = ImageType([GREYSCALE, RGB])
self_type = ImageType([GREYSCALE, RGB])
args = Args(ImageType([ONEBIT], "mask"))
class to_nested_list(PluginFunction):
"""
Converts an image to a nested Python list.
This method is the inverse of ``nested_list_to_image``.
The following table describes how each image type is converted to
Python types:
- ONEBIT -> int
- GREYSCALE -> int
- GREY16 -> int
- RGB -> RGBPixel
- FLOAT -> float
NOTE: This will not scale very well and should only be used for
small images, such as convolution kernels.
"""
category = "Utility/NestedLists"
self_type = ImageType(ALL)
return_type = Class("nested_list")
doc_examples = [(ONEBIT,)]
class nested_list_to_image(PluginFunction):
"""
Converts a nested Python list to an Image. Is the inverse of
``to_nested_list``.
*nested_list*
A nested Python list in row-major order. If the list is a flat list,
an image with a single row will be created.
*image_type*
The resulting image type. Should be one of the integer Image type
constants (ONEBIT, GREYSCALE, GREY16, RGB, FLOAT). If image_type
is not provided or less than 0, the image type will be determined
by auto-detection from the list. The following list shows the mapping
from Python type to image type:
- int -> GREYSCALE
- float -> FLOAT
- RGBPixel -> RGB
To obtain other image types, the type number must be explicitly passed.
NOTE: This will not scale very well and should only be used
for small images, such as convolution kernels.
Examples:
.. code:: Python
# Sobel kernel (implicitly will be a FLOAT image)
kernel = nested_list_to_image([[0.125, 0.0, -0.125],
[0.25 , 0.0, -0.25 ],
[0.125, 0.0, -0.125]])
# Single row image (note that nesting is optional)
image = nested_list_to_image([RGBPixel(255, 0, 0),
RGBPixel(0, 255, 0),
RGBPixel(0, 0, 255)])
"""
category = "Utility/NestedLists"
self_type = None
args = Args([Class('nested_list'),
Choice('image_type', ['ONEBIT', 'GREYSCALE',
'GREY16', 'RGB', 'FLOAT'])])
return_type = ImageType(ALL)
def __call__(l, t=-1):
return _image_utilities.nested_list_to_image(l, t)
__call__ = staticmethod(__call__)
class diff_images(PluginFunction):
"""
Returns a color image representing the difference of two images
following the conventions of a number of Unix diff visualization
tools, such as CVS web. Pixels in both images are black. Pixels
in 'self' but not in the given image (\"deleted\" pixels) are red.
Pixels in the given image but not in self (\"inserted\" pixels)
are green.
"""
category = "Combine"
self_type = ImageType(ONEBIT)
args = Args([ImageType(ONEBIT, 'other')])
return_type = ImageType(RGB)
pure_python = True
def __call__(self, other):
from gamera.core import RGBPixel
result = self.to_rgb()
diff = self.subtract_images(other)
result.highlight(diff, RGBPixel(255, 64, 64))
diff = other.subtract_images(self)
result.highlight(diff, RGBPixel(64, 255, 64))
return result
__call__ = staticmethod(__call__)
class mse(PluginFunction):
"""
Calculates the mean square error between two images.
"""
category = "Utility"
self_type = ImageType([RGB])
args = Args([ImageType([RGB])])
return_type = Float()
class reset_onebit_image(PluginFunction):
"""
Resets all black pixel values in a onebit image to one. This
can be necessary e.g. after a CC analysis which sets black
pixels to some other label value.
"""
category="Utility"
self_type = ImageType([ONEBIT])
author = "Christoph Dalitz"
class ccs_from_labeled_image(PluginFunction):
"""
Returns all ``Cc``'s represented by unique labels in the given
onebit image. The bounding boxes are computed as tight as possible.
This is mostly useful for reading manually labeled groundtruth
data from color PNG files in combination with the plugin
colors_to_labels_. Example:
.. code:: Python
labeled = rgb.colors_to_labels()
ccs = labeled.ccs_from_labeled_image()
.. _colors_to_labels: color.html#colors-to-labels
"""
category="Utility"
self_type = ImageType([ONEBIT])
return_type = ImageList("ccs")
author = "Christoph Dalitz and Hasan Yildiz"
class min_max_location(PluginFunction):
"""Returns the minimum and maximum pixel value and their location
in an image. When the min/max value occurs at several locations, only the
location that is closest to the bottom right corner is returned.
Only those pixels are examined that are black in the provided *mask*.
When no *mask* is given, the entire image is examined. The mask can
be useful, e.g., to find the lightest and darkest value in the original
greyscale image belonging to a Cc, as in the following example:
.. code:: Python
onebit = grey.to_onebit()
ccs = onebit.cc_analysis()
# compute min/max of first cc in original greyscale image
(pmin, vmin, pmax, vmax) = grey.min_max_location(ccs[0])
The return value is a tuple of the form *(pmin, vmin, pmax, vmax)* where
*pmin* and *pmax* are the point of the minimum and maximum, respectively,
and *vmin* and *vmax* the corresponding pixel values.
"""
category="Analysis"
self_type = ImageType([GREYSCALE,GREY16,FLOAT])
return_type = Class("min_max_loc")
args = Args([ImageType([ONEBIT], name='mask', default=NoneDefault)])
author = "Christoph Dalitz"
doc_examples = [(GREYSCALE,)]
def __call__(self, mask=None):
if mask is None:
return _image_utilities.min_max_location_nomask(self)
else:
return _image_utilities.min_max_location(self, mask)
__call__ = staticmethod(__call__)
class min_max_location_nomask(PluginFunction):
"""This is only a helper function for overloading min_max_location.
It is not needed on the Python side, but only on the C++ side due to
the plugin wrapping mechanism of Gamera.
"""
category = None
self_type = ImageType([GREYSCALE,GREY16,FLOAT])
return_type = Class("min_max_loc")
author = "Christoph Dalitz"
class UtilModule(PluginModule):
cpp_headers=["image_utilities.hpp"]
category = None
functions = [image_save, image_copy,
histogram, union_images,
fill_white, fill, pad_image, pad_image_default, trim_image,
invert, clip_image, mask,
nested_list_to_image, to_nested_list,
diff_images, mse, reset_onebit_image,
ccs_from_labeled_image,
min_max_location, min_max_location_nomask]
author = "Michael Droettboom and Karl MacMillan"
url = "http://gamera.sourceforge.net/"
module = UtilModule()
union_images = union_images()
nested_list_to_image = nested_list_to_image()
del pad_image_default
|