Skip to content

.Call - applying setAttrib(x, R_DimSymbol, s) to a matrix being an element of a list

6 messages · Simon Urbanek, Brian Ripley, Oleg Sklyar

#
Dear R developers,

I am writing some C code that loads multiple images into a list of R 
matrices. The whole R object is created within the C code. To simplify 
coding, elements of the list are first created as vectors and then 
converted to corresponding matrices using setAttrib(x, R_DimSymbol, s). 
Generally the code works fine except for one detail. Applying setAttrib 
sets ALL elements (matrices) in the list to the same size, namely to the 
one last applied, doesn't matter if other matrices are larger or 
smaller. The command is applied to an element, not to the whole list in 
the following way, see below. Is there any way to change the dims of an 
element without altering other elements in this example? Furthermore, if 
I skip the last command and return a list of vectors instead of a list 
of matrices - all vectors have different lengths corresponding to the 
respective image sizes.

// creating the list to hold matrices
SEXP result = allocVector(VECSXP, nFiles);

// creating a vector for the list element i
SET_VECTOR_ELT(result, i, allocVector(INTSXP, size[0] * size[1]));

// getting a pointer to the element to simlify coding
SEXP element = VECTOR_ELT(result, i);

// writing some values to the element

// converting element into a matrix
setAttrib(element, R_DimSymbol, imgSize);

Thanks in advance for help
Oleg

Dr Oleg Sklyar
European Bioinformatics Institute
Wellcome Trust Genome Campus
Hinxton, Cambridge, CB10 1SD
England
phone/fax  +44(0)1223 49 4478/4468
e-mail osklyar@ebi.ac.uk
#
Oleg,

you gave us only a fragment of your code, so I can only guess what the 
problem is:
On Mar 16, 2005, at 12:40 PM, Oleg Sklyar wrote:

            
What is imgSize? The behavior you describe seems as if you re-using the 
imgSize SEXP in all elements. AFAIR in your case setAttrib doesn't copy 
the value, so you need to do so yourself (or alloc new dim array for 
each element).

Cheers,
Simon
#
Dear Simon,

 > you gave us only a fragment of your code, so I can only guess what 
the problem is:

 > What is imgSize? The behavior you describe seems as if you re-using 
the imgSize SEXP in all elements.
 > AFAIR in your case setAttrib doesn't copy the value, so you need to 
do so yourself (or alloc new dim array
 > for each element).

Sorry, it is always a tradeoff - either to explain or put a relatively 
large code, which also uses non-standard libraries making the code 
difficult to read. imgSize values are reset in between: see the full 
code below. The SEXP pointer imgSize stays in tact, that's true, but its 
values are changed (the problem is, they are always the last in the 
row). I.e. if I have 3 images 40x20, 200x100 and 150x75 I will get three 
matrices of 150x75, but if I omit setAtrib and return vectors I get 
vectors of different length.

Oleg

SEXP load2DImages(SEXP fileNames) {
    int nFiles = LENGTH(fileNames);
    std::cout << "Loading " << nFiles << " files..." << std::endl;
    // SEXP result = allocList(nFiles);
    SEXP result = allocVector(VECSXP, nFiles);
    PROTECT(result);
    SEXP imgSize = allocVector(INTSXP, 2);
    PROTECT(imgSize);
    TRGB2DImage::Pointer image;
    TRGB2DReader::Pointer reader = TRGB2DReader::New();
    TRGB2DImage::SizeType size;   
    TRGB2DImage::IndexType pixIndex;
    for (int i = 0; i < nFiles; i++) {
        try {
            char *filename = CHAR(asChar(VECTOR_ELT(fileNames, i)));
            std::cout << std::endl << "Loading image file " << filename 
<< "... ";
            reader->SetFileName(filename);
            reader->Update();
            image = reader->GetOutput();
        } catch(...) {
            std::cout << "failed!";
            continue;
        }
        std::cout << std::endl;
        size = image->GetLargestPossibleRegion().GetSize();
        INTEGER(imgSize)[0] = size[1];
        INTEGER(imgSize)[1] = size[0];
        std::cout << size[0] << " x " << size[1] << std::endl;
        SET_VECTOR_ELT(result, i, allocVector(INTSXP, size[0] * size[1]));
        SEXP element = VECTOR_ELT(result, i);
        for (int ix = 0; ix < size[0]; ix++) {
            pixIndex[0] = ix;
            for (int iy = 0; iy < size[1]; iy++) {
                pixIndex[1] = iy;
                INTEGER(element)[size[1] * ix + iy] = 
getIntRGBColour(image->GetPixel(pixIndex));
            }
        }
        setAttrib(element, R_DimSymbol, imgSize);
    }
    UNPROTECT(2);
    if (nFiles == 1)
        return VECTOR_ELT(result, 0);
    else
        return result;
}

Dr Oleg Sklyar
European Bioinformatics Institute
Wellcome Trust Genome Campus
Hinxton, Cambridge, CB10 1SD
England
phone/fax  +44(0)1223 49 4478/4468
e-mail osklyar@ebi.ac.uk
Simon Urbanek wrote:

            
#
On Thu, 17 Mar 2005, Oleg Sklyar wrote:

            
So, as Simon says, you need to create separate objects for the dimensions 
of each matrix, not share the object.  It is the object pointed to by 
imgSize you have attached as the dimensions, not the values.

BTW, setAttrib(element, R_DimSymbol, imgSize) just calls dimgets(element, 
imgSize), so I would have used the latter directly.

I believe you do not have three matrices of 150x75, rather a vector of 
length 40*20 with an inconsistent dim attribute of c(150, 75), etc.

  
    
#
Oleg,
On Mar 16, 2005, at 8:34 PM, Oleg Sklyar wrote:

            
Just posting all R related commands is sufficient ;) - in your case 
that would be the single imgSize alloc and the assignments- but the 
full code is fine, too.
That's the problem. As I said previously - you need to duplicate it or 
alloc new ones. setAttrib of non-NAMED values doesn't copy the value, 
so you're passing the same pointer to all your matrices, so they all 
share the same dimension object. This means that whenever you change 
integers the pointer is pointing at, you change it for all matrices you 
used it in, which is probably not what you intended.
The length of the vector has nothing to do with the dimensions - 
technically. In C you can say "this is a vector of 10 integers and has 
dimension 100x200" - although such matrix is invalid, in C you're free 
to do so. It's up to your code to make sure the dimension and the 
vector length match. In your case, they don't and your code is to blame 
;).

Cheers,
Simon
#
Thanks a lot for your comments. In fact I had an idea that maybe it is a 
pointer that is transferred after Simon's yesterday's commnent. So 
generally I should always allocVector for imgSize before passing it and 
just let the garbage collector to clean them afterwards if I understand 
right.

Best regards
Oleg

Dr Oleg Sklyar
European Bioinformatics Institute
Wellcome Trust Genome Campus
Hinxton, Cambridge, CB10 1SD
England
phone/fax  +44(0)1223 49 4478/4468
e-mail osklyar@ebi.ac.uk
Prof Brian Ripley wrote: