Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Comparing changes

Choose two branches to see what's changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
...
Checking mergeability… Don't worry, you can still create the pull request.
  • 3 commits
  • 4 files changed
  • 0 commit comments
  • 1 contributor
Commits on Mar 24, 2013
@zmoratto zmoratto Core: Make blob consolidation step threaded
It takes a long 5 minutes on larger imagery.
6b0a8e5
@zmoratto zmoratto core: Fix blob x shifting on invalid value
The code was sometimes using front() on an empty list. This is
undefined behavior.

 LocalWords:  unstage src config
ac36bcd
Commits on Mar 25, 2013
@zmoratto zmoratto Core: Use a more exhaustive intersect
Instead of just intersecting the bounding bboxes, intersect the actual
shapes.
0811ed7
View
225 src/asp/Core/BlobIndexThreaded.cc
@@ -20,15 +20,11 @@
///
#include <asp/Core/BlobIndexThreaded.h>
+#include <boost/foreach.hpp>
using namespace vw;
using namespace blob;
-// BLOB COMPRESSED
-///////////////////////////////////////////
-
-// shift_x(..)
-//----------------------------
void BlobCompressed::shift_x( int32 const& value ) {
for ( uint32 i = 0; i < m_row_end.size(); i++ )
for ( std::list<int32>::iterator iter_start = m_row_start[i].begin(),
@@ -40,8 +36,6 @@ void BlobCompressed::shift_x( int32 const& value ) {
m_min[0] += value;
}
-// refactor()
-//----------------------------
void BlobCompressed::refactor() {
for ( uint32 i = 0; i < m_row_end.size(); i++ ) {
std::list<int32>::iterator iter_n_start = m_row_start[i].begin(),
@@ -59,8 +53,20 @@ void BlobCompressed::refactor() {
}
}
-// size()
-//----------------------------
+BlobCompressed::BlobCompressed( vw::Vector2i const& top_left,
+ std::vector<std::list<vw::int32> > const& row_start,
+ std::vector<std::list<vw::int32> > const& row_end ) :
+ m_min(top_left), m_row_start(row_start), m_row_end(row_end) {
+ VW_DEBUG_ASSERT( row_start.size() == row_end.size(),
+ vw::InputErr() << "Input vectors do not have the same length." );
+ for ( size_t i = 0; i < row_start.size(); i++ ) {
+ VW_DEBUG_ASSERT( row_start[i].size() == row_end[i].size(),
+ vw::InputErr() << "List at row " << i << " doesn't have matched starts and ends." );
+ }
+}
+
+BlobCompressed::BlobCompressed() : m_min(-1,-1) {}
+
int32 BlobCompressed::size() const {
int32 sum = 0;
for ( uint32 r = 0; r < m_row_end.size(); r++ )
@@ -71,8 +77,6 @@ int32 BlobCompressed::size() const {
return sum;
}
-// bounding_box()
-//----------------------------
BBox2i BlobCompressed::bounding_box() const {
BBox2i bbox;
bbox.min() = m_min;
@@ -86,8 +90,26 @@ BBox2i BlobCompressed::bounding_box() const {
return bbox;
}
-// is_on_right(..) (8 connected)
-//---------------------------
+bool BlobCompressed::intersects( vw::BBox2i const& input ) const {
+ // Check if Y's overlap.
+ if ( input.max().y() <= m_min.y() ||
+ input.min().y() >= m_min.y() + int32(m_row_start.size()) )
+ return false;
+
+ // Check X for each row.
+ for ( size_t i = 0; i < m_row_start.size(); i++ ) {
+ if ( !m_row_start[i].size() ||
+ m_min.y() + int32(i) < input.min().y() ||
+ m_min.y() + int32(i) >= input.max().y() )
+ continue;
+ if ( m_row_end[i].back() + m_min.x() > input.min().x() &&
+ m_row_start[i].front() + m_min.x() < input.max().x() ) {
+ return true;
+ }
+ }
+ return false;
+}
+
bool BlobCompressed::is_on_right( BlobCompressed const& right ) const {
int32 y_offset = m_min.y()-right.min().y()-1;
// Starting r_i on the index above
@@ -96,22 +118,20 @@ bool BlobCompressed::is_on_right( BlobCompressed const& right ) const {
i++, r_i++ ) {
if ( r_i >= 0 && r_i < int32(right.num_rows()) )
if ( m_row_end[i].back() + m_min.x() ==
- right.start(r_i).front()+right.min().x() )
+ right.m_row_start[r_i].front()+right.min().x() )
return true;
if ( r_i+1 >= 0 && r_i+1 < int32(right.num_rows()) )
if ( m_row_end[i].back() + m_min.x() ==
- right.start(r_i+1).front()+right.min().x() )
+ right.m_row_start[r_i+1].front()+right.min().x() )
return true;
if ( r_i+2 >= 0 && r_i+2 < int32(right.num_rows()) )
if ( m_row_end[i].back() + m_min.x() ==
- right.start(r_i+2).front()+right.min().x() )
+ right.m_row_start[r_i+2].front()+right.min().x() )
return true;
}
return false;
}
-// is_on_bottom(..) (8 connected)
-//---------------------------
bool BlobCompressed::is_on_bottom( BlobCompressed const& bottom ) const {
if ( bottom.min().y() != m_min.y()+int32(m_row_start.size()) )
return false;
@@ -119,8 +139,8 @@ bool BlobCompressed::is_on_bottom( BlobCompressed const& bottom ) const {
for ( std::list<int32>::const_iterator top_start = m_row_start.back().begin(),
top_end = m_row_end.back().begin(); top_start != m_row_start.back().end();
top_start++, top_end++ )
- for ( std::list<int32>::const_iterator bot_start = bottom.start(0).begin(),
- bot_end = bottom.end(0).begin(); bot_start != bottom.start(0).end();
+ for ( std::list<int32>::const_iterator bot_start = bottom.m_row_start[0].begin(),
+ bot_end = bottom.m_row_end[0].begin(); bot_start != bottom.m_row_start[0].end();
bot_start++, bot_end++ ) {
if ( (*top_end+m_min.x() >= *bot_start + bottom.min().x()) &&
(*top_start+m_min.x() <= *bot_end+bottom.min().x()) )
@@ -129,8 +149,6 @@ bool BlobCompressed::is_on_bottom( BlobCompressed const& bottom ) const {
return false;
}
-// add_row(..)
-//----------------------------
void BlobCompressed::add_row( Vector2i const& start,
int const& width ) {
if ( m_min[0] == -1 ) {
@@ -166,16 +184,14 @@ void BlobCompressed::add_row( Vector2i const& start,
}
}
-// absorb(..)
-//-----------------------------
void BlobCompressed::absorb( BlobCompressed const& victim ) {
// First check to see if I'm empty
if ( m_row_start.empty() ) {
m_min = victim.min();
for ( int i = 0; i < victim.num_rows(); i++ ) {
- m_row_start.push_back( victim.start(i) );
- m_row_end.push_back( victim.end(i) );
+ m_row_start.push_back( victim.m_row_start[i] );
+ m_row_end.push_back( victim.m_row_end[i] );
}
return;
}
@@ -192,8 +208,8 @@ void BlobCompressed::absorb( BlobCompressed const& victim ) {
// Inserting victim's connected rows in singletons at a time
// become sometimes they connect like zippers.
- for ( std::list<int32>::const_iterator v_singleton_start = victim.start(i).begin(),
- v_singleton_end = victim.end(i).begin(); v_singleton_start != victim.start(i).end();
+ for ( std::list<int32>::const_iterator v_singleton_start = victim.m_row_start[i].begin(),
+ v_singleton_end = victim.m_row_end[i].begin(); v_singleton_start != victim.m_row_start[i].end();
v_singleton_start++, v_singleton_end++ ) {
std::list<int32>::iterator start_insertion_point = m_row_start[m_index].begin(),
@@ -267,8 +283,8 @@ void BlobCompressed::absorb( BlobCompressed const& victim ) {
std::vector<std::list<int32> > temp_end;
for ( int v_i = 0; v_i < m_min.y()-victim.min().y(); v_i++ ) {
if ( v_i < victim.num_rows() ) {
- temp_start.push_back(victim.start(v_i));
- temp_end.push_back(victim.end(v_i));
+ temp_start.push_back(victim.m_row_start[v_i]);
+ temp_end.push_back(victim.m_row_end[v_i]);
for ( std::list<int32>::iterator i_start = temp_start.back().begin(),
i_stop = temp_end.back().begin();
i_start != temp_start.back().end(); i_start++, i_stop++ ) {
@@ -303,8 +319,8 @@ void BlobCompressed::absorb( BlobCompressed const& victim ) {
temp_start.push_back(std::list<int32>());
temp_end.push_back(std::list<int32>());
} else {
- temp_start.push_back( victim.start(v_i) );
- temp_end.push_back( victim.end(v_i) );
+ temp_start.push_back( victim.m_row_start[v_i] );
+ temp_end.push_back( victim.m_row_end[v_i] );
for ( std::list<int32>::iterator i_start = temp_start.back().begin(),
i_stop = temp_end.back().begin();
i_start != temp_start.back().end(); i_start++, i_stop++ ) {
@@ -321,8 +337,14 @@ void BlobCompressed::absorb( BlobCompressed const& victim ) {
temp_end.end() );
}
// Recalculate min.x()
- int32 lowest_value = 0;
- for ( uint32 i = 0; i < m_row_start.size(); i++ ) {
+ size_t valid_first = 0;
+ while ( valid_first < m_row_start.size() ) {
+ if ( m_row_start[valid_first].size() )
+ break;
+ valid_first++;
+ }
+ int32 lowest_value = m_row_start[valid_first].front();
+ for (size_t i = ++valid_first; i < m_row_start.size(); i++ ) {
if ( m_row_start[i].front() < lowest_value )
lowest_value = m_row_start[i].front();
}
@@ -330,8 +352,6 @@ void BlobCompressed::absorb( BlobCompressed const& victim ) {
this->refactor();
}
-// decompress(..)
-//-------------------------
void BlobCompressed::decompress( std::list<Vector2i>& output ) const {
output.clear();
for (int32 r = 0; r < int32(m_row_end.size()); r++ )
@@ -342,19 +362,82 @@ void BlobCompressed::decompress( std::list<Vector2i>& output ) const {
output.push_back( Vector2i(c,r)+m_min );
}
-// BLOB INDEX CUSTOM
-////////////////////////////////////////////
-// ( unfortunately they're templated )
+void BlobCompressed::print() const {
+ vw::vw_out() << "BlobCompressed | min: " << m_min << "\n";
+ for ( vw::uint32 i = 0; i < m_row_start.size(); i++ ) {
+ vw::vw_out() << " " << i << "|";
+ for ( std::list<vw::int32>::const_iterator s_iter = m_row_start[i].begin(),
+ e_iter = m_row_end[i].begin(); s_iter != m_row_start[i].end();
+ s_iter++, e_iter++ )
+ vw::vw_out() << "(" << *s_iter << "<>" << *e_iter << ")";
+ vw::vw_out() <<"\n";
+ }
+}
-// BLOB INDEX TASK
-////////////////////////////////////////////
-// ( unfortunately they're templated )
+class ConsolidateAbsorbTask : public Task, private boost::noncopyable {
+ std::deque<BlobCompressed> &m_src_c_blob, &m_dest_c_blob;
+ std::deque<vw::BBox2i>& m_dest_blob_bbox;
+ std::vector<uint32>& m_lut;
+ int m_max_area;
+ uint32 m_start_index, m_end_index;
+ Mutex& m_append_mutex;
+public:
+ ConsolidateAbsorbTask( std::deque<BlobCompressed>& src_c_blob,
+ std::deque<BlobCompressed>& dest_c_blob,
+ std::deque<vw::BBox2i>& dest_blob_bbox,
+ std::vector<uint32>& lut, int max_area,
+ uint32 start, uint32 end, Mutex& mutex ) :
+ m_src_c_blob(src_c_blob), m_dest_c_blob(dest_c_blob),
+ m_dest_blob_bbox(dest_blob_bbox), m_lut(lut),
+ m_max_area(max_area), m_start_index(start),
+ m_end_index(end), m_append_mutex(mutex) {}
+
+ void operator()() {
+ std::deque<BlobCompressed> result_blobs;
+ std::deque<BBox2i> result_bbox;
+
+ std::vector<std::list<uint32> > absorbtion_list( m_end_index - m_start_index );
+
+ // Build the absorbtion list so that we don't have to keep doing
+ // an exhaustive search over m_lut.x
+ for ( size_t i = 0; i < m_lut.size(); i++ ) {
+ if ( m_lut[i] >= m_start_index && m_lut[i] < m_end_index )
+ absorbtion_list[m_lut[i] - m_start_index].push_back(i);
+ }
-// BLOB INDEX THREADED
-////////////////////////////////////////////
+ // Build the new consolidated blobs
+ for ( size_t i = 0; i < absorbtion_list.size(); i++ ) {
+ // 1: Check to see that the size is going to be less that the
+ // maximium allowed area. Early exit condition.
+ if ( m_max_area > 0 ) {
+ int32 total_size = 0;
+ BOOST_FOREACH( uint32 src_idx, absorbtion_list[i] ) {
+ total_size += m_src_c_blob[src_idx].size();
+ if ( total_size > m_max_area )
+ break;
+ }
+ if ( total_size > m_max_area )
+ continue;
+ }
+
+ // 2: Start absorbing!
+ BlobCompressed current_blob;
+ BOOST_FOREACH( uint32 src_idx, absorbtion_list[i] ) {
+ current_blob.absorb( m_src_c_blob[src_idx] );
+ }
+
+ // 3: Generate the new bounding bbox & Append the results
+ result_blobs.push_back( current_blob );
+ result_bbox.push_back( current_blob.bounding_box() );
+ }
+
+ // Finally append the complete results back to the master lists.
+ Mutex::Lock lock( m_append_mutex );
+ m_dest_c_blob.insert( m_dest_c_blob.end(), result_blobs.begin(), result_blobs.end() );
+ m_dest_blob_bbox.insert( m_dest_blob_bbox.end(), result_bbox.begin(), result_bbox.end() );
+ }
+};
-// consolidate( .. )
-//----------------------------
void BlobIndexThreaded::consolidate( Vector2i const& image_size,
Vector2i const& proc_block_size ) {
// Working out divisors
@@ -417,34 +500,26 @@ void BlobIndexThreaded::consolidate( Vector2i const& image_size,
}
}
- { // Reorder on graph connections
- std::vector<uint32> component(boost::num_vertices(connections));
- int final_num = boost::connected_components(connections,&component[0]);
-
- std::deque<BlobCompressed> blob_temp(final_num);
- for ( uint32 i = 0; i < component.size(); i++ ) {
- uint32 dest = component[i];
- blob_temp[dest].absorb( m_c_blob[i] );
- }
- m_c_blob = blob_temp;
- }
- if ( m_max_area > 0 ) {
- for ( std::deque<BlobCompressed>::iterator blob_it = m_c_blob.begin();
- blob_it != m_c_blob.end(); blob_it++ ) {
- if ( blob_it->size() >= m_max_area ) {
- blob_it = m_c_blob.erase( blob_it );
- blob_it--;
- }
- }
- }
-
- // Rebuild bounding boxes
- m_blob_bbox.resize( m_c_blob.size() );
- std::deque<BlobCompressed>::const_iterator blob_it = m_c_blob.begin();
- std::deque<vw::BBox2i>::iterator bbox_it = m_blob_bbox.begin();
- while ( bbox_it != m_blob_bbox.end() ) {
- *bbox_it = blob_it->bounding_box();
- blob_it++;
- bbox_it++;
+ std::vector<uint32> component(boost::num_vertices(connections));
+ int final_num = boost::connected_components(connections,&component[0]);
+
+ // Spawn threads to coagulate blobs. Creating 2x max number threads
+ // jobs incase the individual jobs are not evenally distributed with
+ // short-circuit conditions like max_area.
+ FifoWorkQueue absorb_queue;
+ m_blob_bbox.clear();
+ std::deque<BlobCompressed> new_c_blob;
+ int number_of_jobs = vw_settings().default_num_threads() * 2;
+ Mutex append_mutex;
+ for ( int j = 0; j < number_of_jobs; j++ ) {
+ int min = ( final_num * j ) / number_of_jobs;
+ int max = ( final_num * (j+1) ) / number_of_jobs;
+ boost::shared_ptr<Task> absorb_task(
+ new ConsolidateAbsorbTask( m_c_blob, new_c_blob,
+ m_blob_bbox, component, m_max_area,
+ min, max, append_mutex ) );
+ absorb_queue.add_task( absorb_task );
}
+ absorb_queue.join_all();
+ m_c_blob = new_c_blob;
}
View
45 src/asp/Core/BlobIndexThreaded.h
@@ -60,9 +60,8 @@ namespace blob {
public:
BlobCompressed( vw::Vector2i const& top_left,
std::vector<std::list<vw::int32> > const& row_start,
- std::vector<std::list<vw::int32> > const& row_end ) :
- m_min(top_left), m_row_start(row_start), m_row_end(row_end) {}
- BlobCompressed() { m_min = vw::Vector2i(-1,-1); }
+ std::vector<std::list<vw::int32> > const& row_end );
+ BlobCompressed();
// Standard Access point
vw::Vector2i const& min() const { return m_min; }
@@ -72,8 +71,9 @@ namespace blob {
std::list<vw::int32> const& end( vw::uint32 const& index ) const { return m_row_end[index]; }
vw::int32 size() const; // Please use sparingly
vw::BBox2i bounding_box() const;
+ bool intersects( vw::BBox2i const& input ) const;
- // Rather specific conditionals used by BlobIndexThreaded
+ // Specific conditionals used by BlobIndexThreaded
bool is_on_right( BlobCompressed const& right ) const;
bool is_on_bottom( BlobCompressed const& bottom ) const;
@@ -81,20 +81,10 @@ namespace blob {
void add_row( vw::Vector2i const& start, int const& width );
// Use to expand this blob into a non overlapped area
void absorb( BlobCompressed const& victim );
- // Dump into stupid format
+ // Dump listing of every pixel used
void decompress( std::list<vw::Vector2i>& output ) const;
-
- void print() const {
- vw::vw_out() << "BlobCompressed | min: " << m_min << "\n";
- for ( vw::uint32 i = 0; i < m_row_start.size(); i++ ) {
- vw::vw_out() << " " << i << "|";
- for ( std::list<vw::int32>::const_iterator s_iter = m_row_start[i].begin(),
- e_iter = m_row_end[i].begin(); s_iter != m_row_start[i].end();
- s_iter++, e_iter++ )
- vw::vw_out() << "(" << *s_iter << "<>" << *e_iter << ")";
- vw::vw_out() <<"\n";
- }
- }
+ // Print internal data
+ void print() const;
};
// Blob Index Custom
@@ -288,11 +278,7 @@ namespace blob {
/////////////////////////////////////
// A task wrapper to allow threading
template <class SourceT>
- class BlobIndexTask : public vw::Task {
- // Disable copy !!
- BlobIndexTask(BlobIndexTask& copy){}
- void operator=(BlobIndexTask& copy) {}
-
+ class BlobIndexTask : public vw::Task, private boost::noncopyable {
vw::ImageViewBase<SourceT> const& m_view;
vw::BBox2i const& m_bbox;
vw::Mutex& m_append_mutex;
@@ -362,8 +348,8 @@ class BlobIndexThreaded {
// Constructor does most of the processing work
template <class SourceT>
BlobIndexThreaded( vw::ImageViewBase<SourceT> const& src,
- vw::int32 const& max_area = 0,
- vw::int32 const& tile_size = vw::vw_settings().default_tile_size() )
+ vw::int32 const& max_area = 0,
+ vw::int32 const& tile_size = vw::vw_settings().default_tile_size() )
: m_max_area(max_area), m_tile_size(tile_size) {
// User needs to remember to give a pixel mask'd input
@@ -373,9 +359,8 @@ class BlobIndexThreaded {
vw::FifoWorkQueue queue(vw::vw_settings().default_num_threads());
typedef blob::BlobIndexTask<SourceT> task_type;
- std::vector<vw::BBox2i> bboxes = image_blocks( src.impl(),
- m_tile_size,
- m_tile_size );
+ std::vector<vw::BBox2i> bboxes =
+ image_blocks( src.impl(), m_tile_size, m_tile_size );
for ( size_t i = 0; i < bboxes.size(); ++i ) {
boost::shared_ptr<task_type> task(new task_type(src, bboxes[i], m_insert_mutex,
m_c_blob, m_blob_bbox,
@@ -392,13 +377,15 @@ class BlobIndexThreaded {
vw::Vector2i( m_tile_size, m_tile_size ) );
// Cull blobs that are too big.
- if ( m_max_area > 0 )
+ if ( m_max_area > 0 ) {
for ( std::deque<blob::BlobCompressed>::iterator iter = m_c_blob.begin();
- iter != m_c_blob.end(); iter++ )
+ iter != m_c_blob.end(); iter++ ) {
if ( iter->size() > m_max_area ) {
iter = m_c_blob.erase( iter );
iter--;
}
+ }
+ }
}
// Access for the users
View
10 src/asp/Core/InpaintView.h
@@ -190,11 +190,11 @@ namespace asp {
std::vector<size_t> intersections;
intersections.reserve(20);
BBox2i bbox_expanded = bbox;
- for ( BlobIndexThreaded::const_bbox_iterator bbox_it = m_bindex.bbox_begin();
- bbox_it != m_bindex.bbox_end(); bbox_it++ ) {
- if ( bbox_it->intersects( bbox ) ) {
- bbox_expanded.grow( *bbox_it );
- intersections.push_back( bbox_it - m_bindex.bbox_begin() );
+ for ( size_t i = 0; i < m_bindex.num_blobs(); i++ ) {
+ if ( m_bindex.blob_bbox(i).intersects( bbox ) && // Early exit option
+ m_bindex.compressed_blob(i).intersects( bbox ) ) {
+ bbox_expanded.grow( m_bindex.blob_bbox(i) );
+ intersections.push_back(i);
}
}
bbox_expanded.expand(1);
View
20 src/asp/Core/tests/TestBlobIndexThreaded.cxx
@@ -21,8 +21,11 @@
#include <vw/FileIO.h>
#include <vw/Image.h>
#include <asp/Core/BlobIndexThreaded.h>
+#include <boost/assign/std/vector.hpp>
+#include <boost/assign/list_of.hpp>
using namespace vw;
+using namespace boost::assign;
TEST(BlobIndexThreaded, TestImage1) {
DiskImageView<PixelGray<uint8> > input("ThreadTest1.tif");
@@ -44,3 +47,20 @@ TEST(BlobIndexThreaded, TestImage3) {
BlobIndexThreaded bindex( create_mask(input,255), 1000, 5 );
EXPECT_EQ( 2u, bindex.num_blobs() );
}
+
+TEST(BlobIndexThreaded, BlobCompressedIntersect) {
+ std::vector<std::list<int32> > starts, ends;
+ starts += list_of(0), list_of(0), list_of(0), list_of(0), list_of(0);
+ ends += list_of(5), list_of(1), list_of(1), list_of(1), list_of(1);
+ blob::BlobCompressed test_blob( Vector2i(5,5), starts, ends );
+
+ test_blob.print();
+
+ EXPECT_FALSE( test_blob.intersects( BBox2i(6,6,2,2) ) );
+ EXPECT_FALSE( test_blob.intersects( BBox2i(4,4,1,1) ) );
+ EXPECT_FALSE( test_blob.intersects( BBox2i(6,4,3,1) ) );
+ EXPECT_FALSE( test_blob.intersects( BBox2i(3,5,2,8) ) );
+ EXPECT_TRUE( test_blob.intersects( BBox2i(6,2,4,10) ) );
+ EXPECT_TRUE( test_blob.intersects( BBox2i(3,4,6,2) ) );
+ EXPECT_TRUE( test_blob.intersects( BBox2i(4,7,2,2) ) );
+}

No commit comments for this range

Something went wrong with that request. Please try again.