Permalink
Browse files

Merge pull request #390 from lgritz/over

ImageBufAlgo::over() Porter/Duff compositing operation and oiiotool --over
by Steven Stavrev (with minor LG mods)
  • Loading branch information...
lgritz committed Jun 28, 2012
2 parents 322245d + 93681dc commit b5b28a9debaec1f270171025b6074d2f342b2183
View
@@ -287,7 +287,7 @@ endif()
# List all the individual testsuite tests here:
oiio_add_tests (ico gpsread misnamed-file nonwhole-tiles
- oiiotool oiiotool-fixnan
+ oiiotool oiiotool-composite oiiotool-fixnan
perchannel
sgi rla psd dpx png
texture-fill texture-filtersize
View
@@ -638,14 +638,20 @@ \section{\oiiotool commands that make new images}
\apiitem{--sub}
Replace the \emph{two} top images with a new image that is the difference
-between the next-to-top and the top image.
+between the first and second images.
\apiend
\apiitem{--abs}
Replace the current image with a new image that has each pixel
consisting of the \emph{absolute value} of he old pixel value.
\apiend
+\apiitem{--over}
+Replace the \emph{two} top images with a new image that is the
+Porter/Duff ``over'' composite of the first image over the second
+image.
+\apiend
+
\apiitem{--flip}
Replace the current image with a new image that is flipped vertically,
with the top scanline becoming the bottom, and vice versa.
View
@@ -52,6 +52,61 @@
OIIO_NAMESPACE_ENTER
{
+class ImageBuf;
+
+
+
+/// Helper struct describing a region of interest in an image.
+/// The region is [xbegin,xend) x [begin,yend) x [zbegin,zend),
+/// with the "end" designators signifying one past the last pixel,
+/// a la STL style.
+struct ROI {
+ int xbegin, xend, ybegin, yend, zbegin, zend;
+ bool defined;
+
+ /// Default constructor is an undefined region.
+ ///
+ ROI () : defined(false) { }
+
+ /// Constructor with an explicitly defined region.
+ ///
+ ROI (int xbegin, int xend, int ybegin, int yend, int zbegin=0, int zend=1)
+ : xbegin(xbegin), xend(xend), ybegin(ybegin), yend(yend),
+ zbegin(zbegin), zend(zend), defined(true)
+ { }
+
+ // Region dimensions.
+ int width () const { return xend - xbegin; }
+ int height () const { return yend - ybegin; }
+ int depth () const { return zend - zbegin; }
+ /// Total number of pixels in the region.
+ imagesize_t npixels () const {
+ imagesize_t w = width(), h = height(), d = depth();
+ return w*h*d;
+ }
+};
+
+
+/// Union of two regions, the smallest region containing both.
+ROI roi_union (const ROI &A, const ROI &B);
+
+/// Intersection of two regions.
+ROI roi_intersection (const ROI &A, const ROI &B);
+
+/// Return pixel data window for this ImageSpec as a ROI.
+ROI get_roi (const ImageSpec &spec);
+
+/// Return full/display window for this ImageSpec as a ROI.
+ROI get_roi_full (const ImageSpec &spec);
+
+/// Set pixel data window for this ImageSpec to a ROI.
+void set_roi (ImageSpec &spec, const ROI &newroi);
+
+/// Set full/display window for this ImageSpec to a ROI.
+void set_roi_full (ImageSpec &spec, const ROI &newroi);
+
+
+
/// An ImageBuf is a simple in-memory representation of a 2D image. It
/// uses ImageInput and ImageOutput underneath for its file I/O, and has
/// simple routines for setting and getting individual pixels, that
@@ -424,6 +479,8 @@ class DLLPUBLIC ImageBuf {
/// aren't local.
void *pixeladdr (int x, int y, int z);
+ /// Is this ImageBuf object initialized?
+ bool initialized () const { return m_spec_valid || m_pixels_valid; }
/// Templated class for referring to an individual pixel in an
/// ImageBuf, iterating over the pixels of an ImageBuf, or iterating
@@ -480,6 +537,20 @@ class DLLPUBLIC ImageBuf {
m_rng_zend = std::min (zend, m_img_zend);
pos (m_rng_xbegin, m_rng_ybegin, m_rng_zbegin);
}
+ /// Construct read-write clamped valid iteration region from
+ /// ImageBuf and ROI.
+ Iterator (ImageBuf &ib, const ROI &roi)
+ : m_ib(&ib), m_tile(NULL)
+ {
+ init_ib ();
+ m_rng_xbegin = std::max (roi.xbegin, m_img_xbegin);
+ m_rng_xend = std::min (roi.xend, m_img_xend);
+ m_rng_ybegin = std::max (roi.ybegin, m_img_ybegin);
+ m_rng_yend = std::min (roi.yend, m_img_yend);
+ m_rng_zbegin = std::max (roi.zbegin, m_img_zbegin);
+ m_rng_zend = std::min (roi.zend, m_img_zend);
+ pos (m_rng_xbegin, m_rng_ybegin, m_rng_zbegin);
+ }
/// Construct from an ImageBuf and designated region -- iterate
/// over region, starting with the upper left pixel, and do NOT
/// clamp the region to the valid image pixels. If "unclamped"
@@ -745,6 +816,20 @@ class DLLPUBLIC ImageBuf {
m_rng_zend = std::min (zend, m_img_zend);
pos (m_rng_xbegin, m_rng_ybegin, m_rng_zbegin);
}
+ /// Construct read-only clamped valid iteration region
+ /// from ImageBuf and ROI.
+ ConstIterator (ImageBuf &ib, const ROI &roi)
+ : m_ib(&ib), m_tile(NULL)
+ {
+ init_ib ();
+ m_rng_xbegin = std::max (roi.xbegin, m_img_xbegin);
+ m_rng_xend = std::min (roi.xend, m_img_xend);
+ m_rng_ybegin = std::max (roi.ybegin, m_img_ybegin);
+ m_rng_yend = std::min (roi.yend, m_img_yend);
+ m_rng_zbegin = std::max (roi.zbegin, m_img_zbegin);
+ m_rng_zend = std::min (roi.zend, m_img_zend);
+ pos (m_rng_xbegin, m_rng_ybegin, m_rng_zbegin);
+ }
/// Construct from an ImageBuf and designated region -- iterate
/// over region, starting with the upper left pixel, and do NOT
/// clamp the region to the valid image pixels. If "unclamped"
View
@@ -42,6 +42,7 @@
#include "imagebuf.h"
#include "fmath.h"
#include "color.h"
+#include "thread.h"
#ifndef __OPENCV_CORE_TYPES_H__
@@ -305,6 +306,100 @@ bool DLLPUBLIC capture_image (ImageBuf &dst, int cameranum = 0,
TypeDesc convert=TypeDesc::UNKNOWN);
+
+/// Set R to the composite of A over B using the Porter/Duff definition
+/// of "over", returning true upon success and false for any of a
+/// variety of failures (as described below).
+///
+/// A and B must have valid alpha channels identified by their ImageSpec
+/// alpha_channel field, with the following two exceptions: (a) a
+/// 3-channel image with no identified alpha will be assumed to be RGB,
+/// alpha == 1.0; (b) a 4-channel image with no identified alpha will be
+/// assumed to be RGBA with alpha in channel [3]. If A or B do not have
+/// alpha channels (as determined by those rules) or if the number of
+/// non-alpha channels do not match between A and B, over() will fail,
+/// returning false.
+///
+/// R is not already an initialized ImageBuf, it will be sized to
+/// encompass the minimal rectangular pixel region containing the union
+/// of the defined pixels of A and B, and with a number of channels
+/// equal to the number of non-alpha channels of A and B, plus an alpha
+/// channel. However, if R is already initialized, it will not be
+/// resized, and the "over" operation will apply to its existing pixel
+/// data window. In this case, R must have an alpha channel designated
+/// and must have the same number of non-alpha channels as A and B,
+/// otherwise it will fail, returning false.
+///
+/// 'roi' specifies the region of R's pixels which will be computed;
+/// existing pixels outside this range will not be altered. If not
+/// specified, the default ROI value will be interpreted as a request to
+/// apply "A over B" to the entire region of R's pixel data.
+///
+/// A, B, and R need not perfectly overlap in their pixel data windows;
+/// pixel values of A or B that are outside their respective pixel data
+/// window will be treated as having "zero" (0,0,0...) value.
+///
+/// threads == 0, the default, indicates that over() should use as many
+/// CPU threads as are specified by the global OIIO "threads" attribute.
+/// Note that this is not a guarantee, for example, the implementation
+/// may choose to spawn fewer threads for images too small to make a
+/// large number of threads to be worthwhile. Values of threads > 0 are
+/// a request for that specific number of threads, with threads == 1
+/// guaranteed to not spawn additional threads (this is especially
+/// useful if over() is being called from one thread of an
+/// already-multithreaded program).
+bool DLLPUBLIC over (ImageBuf &R, const ImageBuf &A, const ImageBuf &B,
+ ROI roi = ROI(), int threads = 0);
+
+
+
+
+/// Helper template for generalized multithreading for image processing
+/// functions. Some function/functor f is applied to every pixel the
+/// region of interest roi, dividing the region into multiple threads if
+/// threads != 1. Note that threads == 0 indicates that the number of
+/// threads should be as set by the global OIIO "threads" attribute.
+///
+/// Most image operations will require additional arguments, including
+/// additional input and output images or other parameters. The
+/// parallel_image template can still be used by employing the
+/// boost::bind (or std::bind, for C++11). For example, suppose you
+/// have an image operation defined as:
+/// void my_image_op (ImageBuf &out, const ImageBuf &in,
+/// float scale, ROI roi);
+/// Then you can parallelize it as follows:
+/// ImageBuf R /*result*/, A /*input*/;
+/// ROI roi = get_roi (R);
+/// parallel_image (boost::bind(my_image_op,boost::ref(R),
+/// boost::cref(A),3.14,_1), roi);
+///
+template <class Func>
+void
+parallel_image (Func f, ROI roi, int nthreads=0)
+{
+ // Special case: threads <= 0 means to use the "threads" attribute
+ if (nthreads <= 0)
+ OIIO::getattribute ("threads", nthreads);
+
+ if (nthreads <= 1 || roi.npixels() < 1000) {
+ // Just one thread, or a small image region: use this thread only
+ f (roi);
+ } else {
+ // Spawn threads by dividing the region into y bands.
+ boost::thread_group threads;
+ int blocksize = std::max (1, (roi.height() + nthreads - 1) / nthreads);
+ int roi_ybegin = roi.ybegin;
+ int roi_yend = roi.yend;
+ for (int i = 0; i < nthreads; i++) {
+ roi.ybegin = roi_ybegin + i * blocksize;
+ roi.yend = std::min (roi.ybegin + blocksize, roi_yend);
+ threads.add_thread (new boost::thread (f, roi));
+ }
+ threads.join_all ();
+ }
+}
+
+
}; // end namespace ImageBufAlgo
@@ -50,6 +50,74 @@
OIIO_NAMESPACE_ENTER
{
+
+
+ROI
+get_roi (const ImageSpec &spec)
+{
+ return ROI (spec.x, spec.x + spec.width,
+ spec.y, spec.y + spec.height,
+ spec.z, spec.z + spec.depth);
+}
+
+
+
+ROI
+get_roi_full (const ImageSpec &spec)
+{
+ return ROI (spec.full_x, spec.full_x + spec.full_width,
+ spec.full_y, spec.full_y + spec.full_height,
+ spec.full_z, spec.full_z + spec.full_depth);
+}
+
+
+
+void
+set_roi (ImageSpec &spec, const ROI &newroi)
+{
+ spec.x = newroi.xbegin;
+ spec.y = newroi.ybegin;
+ spec.z = newroi.zbegin;
+ spec.width = newroi.width();
+ spec.height = newroi.height();
+ spec.depth = newroi.depth();
+}
+
+
+
+void
+set_roi_full (ImageSpec &spec, const ROI &newroi)
+{
+ spec.full_x = newroi.xbegin;
+ spec.full_y = newroi.ybegin;
+ spec.full_z = newroi.zbegin;
+ spec.full_width = newroi.width();
+ spec.full_height = newroi.height();
+ spec.full_depth = newroi.depth();
+}
+
+
+
+ROI
+roi_union (const ROI &A, const ROI &B)
+{
+ return ROI (std::min (A.xbegin, B.xbegin), std::max (A.xend, B.xend),
+ std::min (A.ybegin, B.ybegin), std::max (A.yend, B.yend),
+ std::min (A.zbegin, B.zbegin), std::max (A.zend, B.zend));
+}
+
+
+
+ROI
+roi_intersection (const ROI &A, const ROI &B)
+{
+ return ROI (std::max (A.xbegin, B.xbegin), std::min (A.xend, B.xend),
+ std::max (A.ybegin, B.ybegin), std::min (A.yend, B.yend),
+ std::max (A.zbegin, B.zbegin), std::min (A.zend, B.zend));
+}
+
+
+
ImageBuf::ImageBuf (const std::string &filename,
ImageCache *imagecache)
: m_name(filename), m_nsubimages(0),
Oops, something went wrong.

0 comments on commit b5b28a9

Please sign in to comment.