Well, after all that I'm not so sure the effort was worth it!  I just happened 
upon the code below in the same large program into which I inserted my original 
run-length-encoding algorithm.  This code goes nearly as fast (0.52 seconds vs 
0.46 seconds, 13% slower), produces identical results, and is much more 
readable (and is code I can't take credit for):

my $im_xy = whichND($im != 0);
my $im_vals = $im->indexND($im_xy)->sever;
for my $id($im_vals->uniq->list) {
$coord = $im_xy->(:,which($im_vals==$id))->qsortvec;
}

Derek

On Aug 22, 2011, at 3:42 PM, Chris Marshall wrote:

> Neat, Derek!
> 
> Of course I couldn't resist trying to tweak.  I managed to
> clean up the 2-D index calculation by using one2nd() with
> the thought of going to an N-D version at some point.
> 
> Then using where instead of the which test seemed to
> simplify the paring down of the valid RLE entries.
> 
> Finally, I noticed that the index generating ops inside
> the for loop are equivalent to some simple array-wise
> operations.
> 
> I didn't come up with a good way to generalize from 2-D
> to N-D for the dimensionality of $im.  Nor was I able to
> proceed further with $coord (although I speculate that
> range might be used...).
> 
> Anyway, here is the modified code:
> 
> use PDL::LiteF;
> 
> my $im = pdl q[
>               [ 0 0 1 1 0 0]
>               [ 2 0 0 1 0 0]
>               [ 2 2 0 0 0 3]
>               [ 0 0 0 0 3 0]
>             ];
> 
> my $vals = $im->flat->qsort;
> my $vind = $im->flat->qsorti;  #because of the qsorts,
> similarly-labeled pixels of interest don't even have to be contiguous
> 
> # apparently whichND has this helper routine exposed
> #
> my ($x,$y) = $im->one2nd($vind);
> 
> my ($v_r_a,$v_r_b) = rle($vals);  #run-length-encode the
> 
> # the run-length is >0 for all valid entries so a where should work for this
> #
> ($v_r_a,$v_r_b) = where $v_r_a, $v_r_b, $v_r_a != 0;
> 
> my $vra_cumsum = cumusumover($v_r_a);
> 
> my ($val,$coord);
> 
> # the selection operations could be parallelized but we would
> # need "ragged arrays" to handle the $coord.  I wonder if range
> # could be used?
> #
> #  $val = $v_r_b->copy;
> #  $end = $vra_cumusum - 1;
> #  $start = $vra_cumusum->rotate(1);
> #  $start((0)) .= 0;
> #
> for my $i ( 0..$v_r_b->nelem-1 ) {
>   $val = $v_r_b->(($i));
>   my ($start,$end);
>   $start = $i ? $vra_cumsum(($i-1)) : 0;  #trinary op needed for the
> first element because of how rle() does counting.
>   $end = $vra_cumsum(($i)) - 1;
>   $coord = $x($start:$end)->glue(1,$y($start:$end))->transpose->qsortvec;
> }
> 
> Cheers,
> Chris
> 
> 
> On Mon, Aug 22, 2011 at 4:31 PM, Derek Lamb <[email protected]> wrote:
>> The following code fragment is representative of something I am doing a lot 
>> of these days:
>> 
>> $im = rfits('im.fits');
>> for $val ($im->uniq->list){
>>        next unless $val;
>>        $coord = whichND($im==$val);
>>        ##do some stuff with $im and $coord and $val
>> }
>> 
>> where $im is a 1024 x 1024 image that is mostly zeroes, but has about 2500 
>> regions of positive AND negative integers, like this but obviously much 
>> bigger:
>> [
>> [ 0 0 1 1 0 0]
>> [ 2 0 0 1 0 0]
>> [ 2 2 0 0 0 3]
>> [ 0 0 0 0 3 0]
>> ]
>> 
>> Many of those regions are small, only a few pixels, but a few are several 
>> hundred pixels big.  The test-for-equality inside the whichND is killing me 
>> on speed: the above code fragment takes nearly 30 seconds to execute, not 
>> counting the rfits.  I thought maybe I could speed things up by only going 
>> through the image once, and creating a %hash whose keys were the integers 
>> (1,2,3 etc in the example above) and the values were a list of coordinates, 
>> like this:
>> 
>> $ndc = ndcoords($im)->clump(1,2);
>> for $i(0..$ndc->dim(1)-1){
>>   ($cx,$cy) = $ndc(:,($i))->list;
>>   next unless $im($cx,$cy);
>>   push @{$hash{$im($cx,$cy)}},$cx,$cy;
>> }
>> 
>> But that is even slower, around 40 seconds.
>> 
>> Finally, I came up with this method involving a few stages of indirection:
>> 
>> ###start
>> use PDL::Math; #not included in PDL::LiteF, needed for floor()
>> my $vals = $im->flat->qsort;
>> my $vind = $im->flat->qsorti;  #because of the qsorts, similarly-labeled 
>> pixels of interest don't even have to be contiguous
>> 
>> my $x=$vind % $im->dim(0);
>> my $y=floor($vind/$im->dim(0));
>> 
>> my ($v_r_a,$v_r_b)=rle($vals);  #run-length-encode the
>> #rle says only the elements up to the first 0 in $v_r_a should be 
>> considered, so we pare those down
>> $v_r_b=$v_r_b(0:which($v_r_a==0)->(0)-1);
>> $v_r_a=$v_r_a(0:which($v_r_a==0)->(0)-1);
>> 
>> my $vra_cumsum = cumusumover($v_r_a);
>> 
>> my ($val,$coord);
>> for my $i(0..$v_r_b->nelem-1){
>>    $val = $v_r_b->(($i));
>>    next unless $val;
>>    my ($start,$end);
>>    $end = $vra_cumsum(($i))-1;
>>    $start = $i ? $vra_cumsum(($i-1)) : 0;  #trinary op needed for the first 
>> element because of how rle() does counting.
>>    $coord = $x($start:$end)->glue(1,$y($start:$end))->transpose->qsortvec;
>> }
>> ###end
>> 
>> The qsortvec at the end there is probably extraneous, but was useful for 
>> comparing the output of this method with my original.  Notice there is no 
>> check for equality inside the loop (or anywhere at all except the $v_r_a and 
>> _b pare-down, and I suppose implicitly in the qsorts).  Everything is 
>> accomplished using slices.  This code fragment takes less than 0.5 seconds, 
>> a factor of 60 improvement in speed. Obviously this will only work for 
>> sparse images like my sample above.  I haven't been careful about 
>> generalizing it to more than 2 dimensions, but that shouldn't be too hard.
>> 
>> I'm posting this in the PDL Cookbook on the wiki.  If you think it's useful 
>> let me know and I'll add it to the distribution, calling it which_all or 
>> something like that.  The trick will be how to return ALL of the instances 
>> of $coord to the user--maybe the hash implementation I tried earlier will 
>> work, but I'm open to other suggestions.
>> 
>> Derek
>> 
>> 
>> _______________________________________________
>> Perldl mailing list
>> [email protected]
>> http://mailman.jach.hawaii.edu/mailman/listinfo/perldl
>> 


_______________________________________________
Perldl mailing list
[email protected]
http://mailman.jach.hawaii.edu/mailman/listinfo/perldl

Reply via email to