Author Topic: Strange Result of crop operation  (Read 4021 times)

Offline georg.viehoever

  • PTeam Member
  • PixInsight Jedi Master
  • ******
  • Posts: 2132
Strange Result of crop operation
« on: 2009 June 12 17:04:14 »
Hi Juan,

I just tried to write a "re-bayer" procedure that changes an RGB image with the original bayer pattern into an image with a single color channel. The result is strange: The crop operation, which is part of the program, produces an a pattern where I get very different R-values from the original. It appears that the function doCrop() does some kind of interpolation that I did not expect.

See the attached screenshot and test code. The left image contains the simulated bayer pattern, the lower right one is the cropped image. The different tones of pixels are obvious.

Kind regards,

Georg

Code: [Select]
#include <pjsr/ImageOp.jsh>
#include <pjsr/SampleType.jsh>
#include <pjsr/UndoFlag.jsh>
#include <pjsr/ColorSpace.jsh>

   // compute target rectangle for image, using the central part defined by lenght factor
function computeTargetRect(image, lengthFactor){
      var width=image.width;
      var height=image.height;
      var newWidth=Math.round(width*lengthFactor);
      var newHeight=Math.round(height*lengthFactor);
      var targetRect=new Rect(newWidth,newHeight);
      targetRect.moveTo((width-newWidth)/2,(height-newHeight)/2);

      return targetRect;
   }  //computeTargetRect()

function cropImage(image1, lengthFactor){
      var targetRect=computeTargetRect(image1,lengthFactor);
      // Cropped images
      image1.resetSelections();
      var cropped1 = new Image( targetRect.width, targetRect.height,
                                 image1.numberOfChannels, image1.colorSpace,
                                 image1.bitsPerSample, image1.sampleType);
      image1.selectedRect = targetRect;
      cropped1.apply( image1 );
      image1.resetSelections();
      return cropped1;
   }
  
  // function to transform it to single color
function doSingleChannel(image){
      if (image.numberOfChannels==1){
         return image;
      }
      var res=new Image( image.width, image.height,
                                 1, ColorSpace_Gray,
                                 image.bitsPerSample, image.sampleType);
      image.resetSelections();
      res.fill(0);
      for(var chan=0;chan<image.numberOfChannels;++chan){
         image.selectedChannel=chan;
         res.apply(image,ImageOp_Add);
      }
      image.resetSelections();
      return res;
   }  // doSingleChannel
  
function main1() {
   // create bayer pattern image
   var image=new Image(3908,2602,3,ColorSpace_RGB,16,SampleType_Integer);
   for (var row=0;row<image.height;++row){
      for (var col=0; col<image.width;++col){
      //simulate bayer pattern
         if(row%2==0){
            if (col%2==0){
               image.setSample(0.2,col,row,0);
               image.setSample(0,col,row,1);
               image.setSample(0,col,row,2);
            }else{
               image.setSample(0.2,col,row,1);
               image.setSample(0,col,row,0);
               image.setSample(0,col,row,2);
            }
         }else{
            if (col%2==0){
               image.setSample(0.2,col,row,1);
               image.setSample(0,col,row,0);
               image.setSample(0,col,row,2);
            }else{
               image.setSample(0.2,col,row,2);
               image.setSample(0,col,row,0);
               image.setSample(0,col,row,1);
            }
         }
      }
   }
  
   var cropped=cropImage(image,0.1);
  
   var single=doSingleChannel(cropped);
  
   var w1 = new ImageWindow( image.width, image.height,
               image.numberOfChannels, image.bitsPerSample,
               image.sampleType!=SampleType_Integer, image.colorSpace==ColorSpace_RGB,
               "Bayer");
   w1.mainView.beginProcess( UndoFlag_NoSwapFile );
   w1.mainView.image.assign( image );
   w1.mainView.endProcess();

   var w2 = new ImageWindow( single.width, single.height,
                     single.numberOfChannels, single.bitsPerSample,
                     single.sampleType!=SampleType_Integer,image.colorSpace==ColorSpace_RGB,
                     "Single" );
   w2.mainView.beginProcess( UndoFlag_NoSwapFile );
   w2.mainView.image.assign( single );
   w2.mainView.endProcess();

   var w3 = new ImageWindow( cropped.width,cropped.height,
                     cropped.numberOfChannels, cropped.bitsPerSample,
                     cropped.sampleType!=SampleType_Integer,cropped.colorSpace==ColorSpace_RGB,
                     "Cropped" );
                    
   w2.mainView.beginProcess( UndoFlag_NoSwapFile );
   w2.mainView.image.assign( single );
   w2.mainView.endProcess();
   w1.show();
   w1.zoomToOptimalFit();
   w2.show();
   w2.zoomToOptimalFit();
   w3.show();
   w3.zoomToOptimalFit();
}

main1();
Georg (6 inch Newton, unmodified Canon EOS40D+80D, unguided EQ5 mount)

Offline Juan Conejero

  • PTeam Member
  • PixInsight Jedi Grand Master
  • ********
  • Posts: 7111
    • http://pixinsight.com/
Re: Strange Result of crop operation
« Reply #1 on: 2009 June 22 08:40:53 »
Hi Georg,

In your script, the w3 window contains unpredictable values since you don't assign any image to its main view image. Lines from 104 to 106 are:

Code: [Select]
   w2.mainView.beginProcess( UndoFlag_NoSwapFile );
   w2.mainView.image.assign( single );
   w2.mainView.endProcess();

but should be:

Code: [Select]
   w3.mainView.beginProcess( UndoFlag_NoSwapFile );
   w3.mainView.image.assign( cropped );
   w3.mainView.endProcess();

With these changes, the script works without any problem.

Given that you are working with 16-bit integer images, your cropImage routine can be rewritten as follows:

Code: [Select]
function cropImage(image1, lengthFactor) {
      var cropped1 = Image.newUIntImage( 16 );
      image1.resetSelections();
      image1.selectedRect = computeTargetRect(image1,lengthFactor);
      cropped1.assign( image1 );
      image1.resetSelections();
      return cropped1;
   }

with the same result, but using a simpler implementation.

As far as I can tell, there is no bug in the Image.crop() and Image.cropTo() methods. These methods are direct interfaces to their PCL C++ counterparts. For example, your cropImage function can be rewritten to use Image.crop():

Code: [Select]
function cropImage(image1, lengthFactor){
      var cropped1 = Image.newUIntImage( 16 );
      image1.resetSelections();
      cropped1.assign( image1 );
      cropped1.selectedRect = computeTargetRect( cropped1, lengthFactor );
      cropped1.crop();
      cropped1.resetSelections();
      return cropped1;
   }

Image.cropTo() is a bit tricky; it probably doesn't work as you think. The prototype of this function is:

Code: [Select]
void Image.cropTo( int leftMargin, int topMargin, int rightMargin, int bottomMargin
   [, Number redOrGray[, Number green[, Number blue[, Number alpha]]]] )

where margins are expressed in pixels. Positive margins are added at their corresponding side of the image, while negative ones crop the image. The optional Number parameters define fill colors for newly created canvas regions, when positive margins are applied.

Alternatively, margins can be specified as a Rect object:

Code: [Select]
void Image.cropTo( Rect margins
   [, Number redOrGray[, Number green[, Number blue[, Number alpha]]]] )

where the x0, y0, x1 and y1 members of margins correspond to the left, top, right and bottom cropping margins, respectively.

Evidently, the descriptions of these methods in the ObjectBrowser tree are incorrect; I've just fixed them for version 1.5.3.

Hope this helps; let me know if you have further problems.
Juan Conejero
PixInsight Development Team
http://pixinsight.com/

Offline georg.viehoever

  • PTeam Member
  • PixInsight Jedi Master
  • ******
  • Posts: 2132
Re: Strange Result of crop operation
« Reply #2 on: 2009 June 22 10:24:07 »
Juan,

thanks for looking into this. The issue I reported was indeed a bug that I created in my code.

The issue with the image.cropTo() documentation explains the problems I had in http://pixinsight.com/forum/index.php?topic=1231.msg6089#msg6089 : I used  cropped1.cropTo(targetRect) with targetRect describing the area that I would like to get, not the margins which apparently it would like to see. I feel that it is a bit counter intuitive, but as long as it is documented ;) .


Georg
Georg (6 inch Newton, unmodified Canon EOS40D+80D, unguided EQ5 mount)