In previous thread "How to use SliceN to convert 2D image to 3D tiles" @BmyGuest has demonstrated two efficient ways other than the direct expressions approach. Here I would like to extend the requirement where the tiles are covering the whole 2D input data and these tiles are allowed to overlap. A schematic drawing showing the scenario, and example code are given below. In this generalized case, I found direct expression is faster than loop-Slice - may be due to now we have to do double loops? I cannot quit figure out how to use the "DataStream" to accomplish this. Thanks ahead for any suggestions.
// $BACKGROUND$
// create a test image
number sizeX = 512, sizeY = sizeX;
number tileSize = 32;
image img := exprsize( sizeX, sizeY, icol % (tileSize+irow));
img.SetName( "data" );
img.ShowImage();
number delta = 4; // tile movement
number nStepX = (sizeX - tileSize - (sizeX-tileSize)%delta) / delta + 1;
number nStepY = (sizeY - tileSize - (sizeY-tileSize)%delta) / delta + 1;
//
number count = nStepX * nStepY;
number rangeX = tileSize + (nStepX-1) * delta;
number rangey = tileSize + (nStepY-1) * delta;
//
result( "\n\n" );
result( "========= started... ========== N = " + count + "\n" );
result( "data: " + sizeX + "x" + sizeY + "\n" );
result( "tileSize = " + tileSize + ", delta=" + delta + "\n" );
result( "nStepX, Y = (" + nStepX + ", " + nStepY + ") steps\n");
result( "rangeX, Y = [" + rangeX + ", " + rangeY + "] pixels\n\n" );
number tickStart ,tickStart2,tickStart3,tickStart4,tickEnd
string fmt = "%.3f sec.";
//
tickStart = GetHighResTickCount();
image test1 := exprsize( tileSize, tileSize, count, \
img[ icol + iplane.mod(nStepX)*delta, irow + floor(iplane/nStepX)*delta ] );
test1.SetName("Tiles (direct)")
Result("Direct exprsize: \n")
tickEnd = GetHighResTickCount()
Result("expression : " + tickStart.CalcHighResSecondsBetween(tickEnd).format(fmt) + "\n" )
Result("TOTAL time : " + tickStart.CalcHighResSecondsBetween(tickEnd).format(fmt) + "\n\n" )
tickStart = GetHighResTickCount()
image test2 := RealImage( "Tiles (Slice2)", 4, tileSize, tileSize, count);
tickStart2 = GetHighResTickCount();
for( number idy = 0; idy < nStepY; idy++ ) {
for( number idx = 0; idx < nStepX; idx++ ) {
test2.slice2( 0,0,idx+idy*nStepX, 0,tileSize,1, 1,tileSize,1 ) = \
img.slice2( idx*delta,idy*delta,0, 0,tileSize,1, 1,tileSize,1 );
};
};
tickEnd = GetHighResTickCount()
Result("Slice2 & Loop: \n")
Result("image creation : " + tickStart.CalcHighResSecondsBetween(tickStart2).format(fmt) + "\n" )
Result("slice2 loop : " + tickStart2.CalcHighResSecondsBetween(tickEnd).format(fmt) + "\n" )
Result("TOTAL time : " + tickStart.CalcHighResSecondsBetween(tickEnd).format(fmt) + "\n\n" )
test2.ShowImage();
image RecoverImage( image src, number nStepX, number nStepY ) {
number d0, d1, d2;
src.Get3DSize(d0,d1,d2);
if( d2 != nStepX * nStepY ) throw( "unexpected error" );
number tic = GetHighResTickCount();
image rst := exprsize( d0*nStepX, d1*nStepY, src[ icol%d0, irow%d1, floor(icol/d0) + floor(irow/d1)*nStepX ] );
number toc = GetHighResTickCount();
Result("Recover data : " + tic.CalcHighResSecondsBetween(toc).format(fmt) + "\n" )
//
rst.SetName( "recovered" );
//
return rst;
};
image rec := test2.RecoverImage( nStepX, nStepY );
rec.ShowImage();
result( "=============== ended =================\n\n" );
In this extended scenario, the sub-sampling is no longer over contiguous memory blocks, so the data streaming approach will also require script-level looping and will probably not be particularly efficient.
On my setup, looping over Slice2 calls is still ~20% faster than the the single call to ExprSize, but that may be because my computer has an old processor. I did notice, however, that the timing results varied a great deal and seemed to be connected to foreground activity in support of the Display floating window. For optimum and consistent timing results, I think it is important to either delay calls to ShowImage until the very end of the script, or to make sure the Display floating window is closed. Of course, these issues are side-stepped entirely if one runs the script on the foreground thread, instead of in the background, as you currently seem to do.