Click here to Skip to main content
Click here to Skip to main content

STL like template based coding with the MMX/SSE extension

By , 17 Nov 2006
 

Introduction

Accelerating with MMX/SSE extension is one of the most effective performance gain techniques in image processing, signal processing and numerical computing. To accelerate your application with MMX/SSE, Intel provides the following solutions:

A combination of these solutions is the best solution. Open CV is the interface part of this combination. However, coding based on OpenCV causes messy and unreadable code. More sophisticated coding with MMX/SSE extension can be achieved by wrapping OpenCV based on STL like code. STL like OpenCV wrapper (STLLCV) is a wrapping of OpenCV which enables STL like readable code. This wrapping is also an interface to the following STL like libraries:

The following are belief explanations about the usage of this wrapping.

Matrix operation wrapping

uBLAS is a matrix operation based on expressional template technique. Expressional template technique enables simple description of complicated matrix operations. The interface to uBLAS enables the same simple description with various OpenCV vraioue functions. The usage of this wrapping is given below.

Usage

#include "stllcv/ublascvmatrix.hxx"
using namespace stllcv;

//init matrix
CublasCvMatrix<float,3,3> A;
A[0][0]=3; A[0][1]=2; A[0][2]=1;
A[1][0]=1; A[1][1]=1; A[1][2]=4;
A[2][0]=3; A[2][1]=2; A[2][2]=5;


CublasCvMatrix<float,3,3> B;

//Multiple as OpenCV matrix
cvMatMul( &(CvMat)A , &(CvMat)A, &(CvMat) B );

//Mutiple as uBLAS matrix
B =boost::numeric::ublas::prod (A,A);

See here, for more information on this class.

Image operation wrapping

Interface to vigra

VIGRA is a STL like image processing library. VIGRA provides various STL style image processing functions. This interface to VIGRA enables STL style image processing with various OpenCV variouse functions. The usage of this wrapping is given below.

Usage

#include "vigra/transformimage.hxx"
#include "vigra/recursiveconvolution.hxx"
#include "stllcv/iplvbasicimageoperation.hxx"
#include "stllcv/iplvbasicimage.hxx"
#include "highgui.h"

using namespace stllcv;

#define PEXTYPE unsigned char
int main(int argc, char * argv[])
{
        char winName[]="srcImg";
        cvNamedWindow( winName, 1 );
        CiplvBasicImage<PEXTYPE> iplvImage1("lena.jpg");
        showIplvBasicImag<PEXTYPE>(&iplvImage1,winName);
        int width1 = iplvImage1.width();
        int height1 =iplvImage1.height();
        CiplvBasicImage<PEXTYPE> iplvImage2( width1*2 ,height1*2);

        std::fill(iplvImage2.begin(),iplvImage2.end(),100);
        iplRotate(iplvImage1.pIplImage,
                  iplvImage2.pIplImage,
                  30.0, 100 ,150 ,IPL_INTER_NN);     
        showIplvBasicImag<PEXTYPE>(&iplvImage2,winName);

        vigra::initImage(
           destIterRange(
             iplvImage2.upperLeft() + vigra::Diff2D(50, 100),
             iplvImage2.upperLeft() + vigra::Diff2D(400, 200))
             ,200);
        showIplvBasicImag<PEXTYPE>(&iplvImage2,winName);

        vigra::transformImage(srcImageRange(iplvImage2),
                       destImage(iplvImage2),
                       vigra::linearIntensityTransform(-1, -255));
        showIplvBasicImag<PEXTYPE>(&iplvImage2,winName);

        CiplvBasicImage<PEXTYPE> iplvImage3tmp( iplvImage2);
        CiplvBasicImage<PEXTYPE> iplvImage3( iplvImage2);

        int scale = 5;
        vigra::recursiveSmoothX(vigra::srcImageRange(iplvImage2),
                                vigra::destImage(iplvImage3tmp), scale);
        vigra::recursiveSmoothY(vigra::srcImageRange(iplvImage3tmp),
                                vigra::destImage(iplvImage3), scale);
        showIplvBasicImag<PEXTYPE>(&iplvImage3,winName);

        saveIplvBasicImag<PEXTYPE>(&iplvImage3, "out.jpg");

        cvDestroyWindow(winName);
        return 0;
}

See here, for more information on this class.

Interface to adobe GIL

Adobe GIL is also STL like image processing library. Usage of our wrapping is given below.

Usage

#include "stllcv/gil_wrap_iplimage.hpp"
#include <iostream>
#include "cv.h"

#include "stllcv/gil_dynamic_wrap_iplimage.hpp"
//including this file icrease complie time

#include "highgui.h"
using namespace gil;
using namespace stllcv;

template <typename Out>
struct halfdiff_cast_channels {
    template <typename T> Out operator()(const T& in1, 
                                    const T& in2) const {
        return Out((in2-in1)/2);}
};

template <typename SrcView, typename DstView>
void x_gradient(const SrcView& src, const DstView& dst) {
    typedef typename DstView::channel_t dst_channel_t;
    for (int y=0; y<src.height(); ++y) {
        typename SrcView::x_iterator src_it = src.row_begin(y);
        typename DstView::x_iterator dst_it = dst.row_begin(y);
        for (int x=1; x<src.width()-1; ++x) {
            transform_channels(src_it[x-1], src_it[x+1], dst_it[x], 
                               halfdiff_cast_channels<dst_channel_t>());}}
}

template <typename DstView>
struct x_gradient_obj {
    typedef void result_type;        // required typedef
    const DstView& _dst;
    x_gradient_obj(const DstView& dst) : _dst(dst) {}
    template <typename SrcView> 
    void operator()(const SrcView& src) 
         const { x_gradient(src, _dst); }
};

template <typename SrcViews, typename DstView>
void x_gradient(any_image_view<SrcViews>& src, const DstView& dst) {
    apply_operation(src, x_gradient_obj<DstView>(dst));
}

template<typename ImageClass>
void show_image(ImageClass &image, char *win_name )
{
    cvShowImage( win_name, image.pIplImage );
        std::cout << "Wait Key Input" << std::endl; 
        cvWaitKey(0);
}

int main(int argc, unsigned char* argv[])
{
    char winName[]="srcImg";
    cvNamedWindow( winName, 1 );
    IplImage *gray_piplimg=cvLoadImage( "test.jpg", 0 );    
    IplImage *color_piplimg=cvLoadImage( "test.jpg");
    int width=gray_piplimg->width;
    int height=gray_piplimg->height;
    int sub_width=115;
    int sub_height=113;

//plz see type naming rule http://opensource.adobe.com/
//        gil/html/giltutorial.html#AppendixConventionSec

//static wrap
    rgb8_planar_view_t  rgb8planarview1    =
       gil_view_from_iplimage<rgb8_planar_ptr_t >(color_piplimg);
    rgb8_planar_view_t  rgb8planarview2    =
       gil_view_from_iplimage<planar_ptr<unsigned char *, 
       rgb_t> >(color_piplimg);
    //planar_color is not supported in some OpenCV's functions.

    bgr8_view_t bgr8view1 =
      gil_view_from_iplimage<bgr8_ptr_t >(color_piplimg);

    //pixel<float ,bgr_t >* == bgr32f_pixel_t* == bgr32f_ptr_t
    bgr32f_view_t    bgr32fview1    =
      gil_view_from_iplimage<bgr32f_ptr_t>(color_piplimg);
    bgr32f_view_t    bgr32fview2    =
      gil_view_from_iplimage<bgr32f_pixel_t *>(color_piplimg);
    bgr32f_view_t    bgr32fview3    =
      gil_view_from_iplimage<pixel<float ,
      bgr_t >*>(color_piplimg);
    
    gil_wrap_iplimage<gray8_pixel_t*>    graywrap1("test.jpg");
    gil_wrap_iplimage<bgr8_ptr_t> colorwrap1(width,height);
    gil_wrap_iplimage<bgr8_ptr_t> colorwrap2(color_piplimg);
    
    std::copy(bgr8view1.begin(),bgr8view1.end(),colorwrap1.begin());
    show_image(colorwrap1,winName);
    x_gradient(bgr8view1,(bgr8_view_t)colorwrap1);
    show_image(colorwrap1,winName);

    bgr8_view_t  bgr_sub_view=subimage_view(bgr8view1, 
                 20,30, sub_width, sub_height);
    gil_wrap_iplimage<bgr8_pixel_t *> 
           color_sub_wrap1(sub_width , sub_height);
    std::copy(bgr_sub_view.begin(),bgr_sub_view.end(),
           color_sub_wrap1.begin());
    show_image(color_sub_wrap1,winName);

//dynamic wrap
//detail of dynamic image  http://opensource.adobe.com/
//         gil/html/giltutorial.html#DynamicImageSec
//This imp is based on gil::any_image_view
    any_ipl_image_view_t color_dynamic_view1(
            gil_dynamic_view_from_iplimage(color_piplimg));
    gil_dynamic_wrap_iplimage<IplImage> 
            color_dynamic_wrap1("test.jpg");
    gil_dynamic_wrap_iplimage<IplImage> 
            color_dynamic_wrap2(width,height);
    gil_dynamic_wrap_iplimage<IplImage> 
            color_dynamic_wrap3(color_piplimg);
    x_gradient(*(color_dynamic_wrap3.any_image_view_ptr),
            (bgr8_view_t)colorwrap1);
        std::cout << "x_gradient_any_view_warp_class "  
                  << std::endl; 
        show_image(colorwrap1,winName);

    cvReleaseImage( &gray_piplimg );    
    cvReleaseImage( &color_piplimg );    
    return 0;
}

Using stllcv

Using downloaded file

  • Install OpenCV to C:\Program Files\OpenCV
  • Install boost 1.33.1 to C:\lib\boost_1_33_1
  • Install vigra 1.4.0 to C:\lib\vigra1.4.0
  • Install adobe::GIL to C:\lib\adobe\gil
  • Unpack downloaded file stllcv_0_7_7.zip to C:\lib\stllcv_0_7_7
  • Open C:\lib\stllcv_0_7_7\src\stllcv_vs2003.sln
  • You can see projects of above examples

Using stllcv with your project

  • Install OpenCV and add its include path (C:\Program Files\OpenCV\cv\include; C:\Program Files\OpenCV\cxcore\include; C:\Program Files\OpenCV\otherlibs\highgui (and C:\Program Files\Intel\plsuite\ (include if you have ipl))
  • Add OpenCV's libpath (C:\Program Files\OpenCV\lib) and add links to cxcore.lib, cv.lib, highgui.lib, (ipl.lib if you have ipl)
  • Install boost and add its include path (C:\lib\boost_1_33_1)
  • Install vigra and add its include path (C:\lib\vigra1.4.0\include)
  • Install adobe::GIL and add its include path (C:\lib\adobe\gil\gil)
  • Unpack downloaded file stllcv_0_7_7.zip and add the include path (C:\lib\stllcv_0_7_7\include)
  • Include appropriate header files based on above examples
  • Then you can use various wrapping class and functions with your project

The current version of stllcv can be downloaded from STL like OpenCV wrapper (STLLCV).

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Hirotaka Niitsuma
Japan Japan
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
Generalpixel type from iterator [modified]memberCoffeeBuzz22 Jun '07 - 3:01 
First off, like to say thanks for providing the GIL-openCV bindings. I've been working on something similar recently.
 
I'm not terribly familiar with GIL yet so I have a couple q's about the implementation.
 
In the top_left_view_from_ipl_image functions, you use "type_from_x_iterator" which provides the view, the xy-locator and the step-iterator types. I think channel_t is suppose to be the pixel type but to get this from iterators I've used
 
std::iterator_traits::value_type
 
Same thing or am I misinterpreting something?
 
--EDIT: actually, planar_rgb_view requires channel_type inputs so that doesn't work. I have to use:
typedef typename boost::gil::type_from_x_iterator::view_t view_t;
typedef typename boost::gil::channel_type::type channel_t;
 
however.
 

Also, out of the three you libs you list, which do you prefer?
 
Cheers,
Chris
 

GeneralRe: pixel type from iteratormemberHirotaka Niitsuma17 Oct '07 - 18:14 
Sorry too late reply
gil proSorry too late reply
gil provides some sample code for usage
I just obey the style of the sample code
In that sample code
type_from_x_iterator::view_t
is used. Thus this is my prefer

GeneralSmall Problem: Dealloc ErrormemberRedFraggle24 Nov '06 - 8:25 
Hi Again Smile | :)
 
In your code, i had problems using:
 
CiplvBasicImage(IPLIMAGE* pIplImageIn);
 
this method does not allocate the image, but the dtor will try to release the (IPL) image.
 
For the moment, i used a boolean to work around this problem:
 
bReleaseMe=true;
in every ctor exept the one above and only release the IPLImage when bReleaseme is true:

~CiplvBasicImage()
{
if(bReleaseMe)
cvReleaseImage( &pIplImage);
}

Maybe, you can include a fix in one of the next releases....
 

btw, i really like your work, thanks again! Smile | :)
GeneralRe: Small Problem: Dealloc Error [modified]memberHirotaka Niitsuma24 Nov '06 - 23:23 
Plz use
CiplvBasicImage(IPLIMAGE IplImageIn);
instead of
CiplvBasicImage(IPLIMAGE* pIplImageIn);
 
Upper function works correctly.
 
And lower will fixed in next version.
 

 
-- modified at 23:26 Saturday 25th November, 2006
QuestionIPL outdated, how to use ippi? (link error for ipl.lib)memberRedFraggle14 Nov '06 - 1:42 
first, thanks a lot for this *very* interesting project!
 
I downloaded the current version (0.7.6) from sourceforge and compiled it with VC7.1 (stllcv_vs2003). I finally succeeded to compile it with Adobe-GIL,boost,jpglib etc. but i don't have ipl. The Intel IPL is outdated now, replaced by ippi, and i still get an error:
 
>LINK : fatal error LNK1104: cannot open file 'ipl.lib'
 
In the sample code, at least one IPL call is done in basicimage..cpp:
 
iplRotate(iplvImage1.pIplImage, iplvImage2.pIplImage,30.0, 100 ,150 ,IPL_INTER_NN);
 

Any suggestions? Work-Arounds? (i already commented out the ipl lines, but examples will work strange then)

AnswerRe: IPL outdated, how to use ippi? (link error for ipl.lib)memberHirotaka Niitsuma14 Nov '06 - 15:06 
You can get ipl from
http://www.cvmt.dk/~hn/Images/install/IPL/[^]
 
but without ipl, this code work except ipl specific function like iplRotate
GeneralSubversion enabledmemberHirotaka Niitsuma5 Nov '06 - 3:02 
Subversion enabled
http://stllcv.svn.sourceforge.net/viewvc/stllcv/stllcv/trunk/
GeneralOpenCV wraped by adobe::gilmemberHirotaka Niitsuma3 Nov '06 - 4:44 
I wrapped OpenCV using adobe::gil
plz see class
 
iplimage_wrapper
 
------------------------
#ifdef WIN32
#define _CRT_SECURE_NO_DEPRECATE 1
#pragma warning(disable : 4244) //
#pragma warning(disable : 4996) // MSFT declared it deprecated
#endif
 
#include
 
#include "cv.h"
#include "highgui.h"
#include "interleaved_ptr.hpp"
 
templateclass CiplImageDepth {public: int depth;CiplImageDepth(){depth= sizeof(PIXELTYPE);}};
template<>class CiplImageDepth {public: int depth;CiplImageDepth(){depth= IPL_DEPTH_8U;}};
template<>class CiplImageDepth {public: int depth;CiplImageDepth(){depth= IPL_DEPTH_8S;}};
template<>class CiplImageDepth {public: int depth;CiplImageDepth(){depth= IPL_DEPTH_16U;}};
template<>class CiplImageDepth {public: int depth;CiplImageDepth(){depth= IPL_DEPTH_16S;}};
template<>class CiplImageDepth {public: int depth;CiplImageDepth(){depth= IPL_DEPTH_32S;}};
template<>class CiplImageDepth {public: int depth;CiplImageDepth(){depth= IPL_DEPTH_32F;}};
template<>class CiplImageDepth {public: int depth;CiplImageDepth(){depth= IPL_DEPTH_64F;}};
 
using namespace gil;
 
templateclass ipl_colormodel_nchannels{public: const int nchannels; ipl_colormodel_nchannels():nchannels(0){} };
template<>class ipl_colormodel_nchannels< gray_t> {public: const int nchannels; ipl_colormodel_nchannels():nchannels(1){} };
template<>class ipl_colormodel_nchannels< bgr_t> {public: const int nchannels; ipl_colormodel_nchannels():nchannels(3){} };
template<>class ipl_colormodel_nchannels< bgra_t> {public: const int nchannels; ipl_colormodel_nchannels():nchannels(4){} };
 

 
template < typename Iterator, typename IPLIMAGE = IplImage>
class iplimage_wrapper : public type_from_x_iterator::view_t
{
public:
IPLIMAGE *pIplImage;
int iplDepthDef;

IPLIMAGE* initProc(int width,int height
)
{
ipl_colormodel_nchannels _colormodel_nchannels_;
CiplImageDepth iplImageDepth;
iplDepthDef=iplImageDepth.depth;
pIplImage=
cvCreateImage( cvSize(
width
, height),
iplDepthDef
, _colormodel_nchannels_.nchannels
);
return pIplImage;
}
 
iplimage_wrapper(int width, int height)
:
type_from_x_iterator::view_t(
interleaved_view(
pIplImage->widthStep /(sizeof(channel_t)*pIplImage->nChannels)
, height
,Iterator( reinterpret_cast( (pIplImage=initProc(width,height) )->imageData ))
,pIplImage->widthStep
)
)
{}
 
~iplimage_wrapper()
{
cvReleaseImage( &pIplImage );
}
};
 

 

 

template
struct halfdiff_cast_channels {
template Out operator()(const T& in1, const T& in2) const {
return Out((in2-in1)/2);
}
};
 
template
void x_gradient(const SrcView& src, const DstView& dst) {
typedef typename DstView::channel_t dst_channel_t;
 
for (int y=0; y<src.height(); ++y) {
typename SrcView::x_iterator src_it = src.row_begin(y);
typename DstView::x_iterator dst_it = dst.row_begin(y);
 
for (int x=1; x());
}
}
}
 

 
int main(int argc, unsigned char* argv[])
{
char winName[]="srcImg";
cvNamedWindow( winName, 1 );
 
IplImage *piplimg;
 
#if 1
piplimg=cvLoadImage( "test.jpg", 0 );
cvShowImage( winName, piplimg );
std::cout << "Wait Key Input" << std::endl;
cvWaitKey(0);

 

typedef interleaved_ptr gray8_interleaved_ptr;
typedef type_from_x_iterator::view_t gray8_interleaved_view_t;
 

 
gray8_interleaved_view_t gray_view=interleaved_view(piplimg->widthStep ,piplimg->height,
gray8_interleaved_ptr(
reinterpret_cast(piplimg->imageData)
),
sizeof(unsigned char)*piplimg->widthStep
);

 

 
iplimage_wrapper iplwrapgray1(piplimg->widthStep ,piplimg->height);
std::copy(gray_view.begin(),gray_view.end(),iplwrapgray1.begin());
cvShowImage( winName, iplwrapgray1.pIplImage );
std::cout << "Wait Key Input" << std::endl;
cvWaitKey(0);
 
x_gradient(gray_view,((gray8_interleaved_view_t)iplwrapgray1));
cvShowImage( winName, iplwrapgray1.pIplImage );
std::cout << "Wait Key Input" << std::endl;
cvWaitKey(0);
 

 
gray8_interleaved_view_t gray_view1=interleaved_view(iplwrapgray1.width() ,iplwrapgray1.height(),
gray8_interleaved_ptr(
reinterpret_cast(iplwrapgray1.pIplImage->imageData)
),
sizeof(unsigned char)* iplwrapgray1.pIplImage->widthStep);
std::copy(gray_view.begin(),gray_view.end(),gray_view1.begin());
cvShowImage( winName, iplwrapgray1.pIplImage );
std::cout << "Wait Key Input" << std::endl;
cvWaitKey(0);
 
x_gradient(gray_view,gray_view1);
cvShowImage( winName, iplwrapgray1.pIplImage );
std::cout << "Wait Key Input" << std::endl;
cvWaitKey(0);
 

 

gray8_interleaved_view_t gray_sub_view=subimage_view(gray_view, 20,30, 40,40);
 

iplimage_wrapper iplwrapgray2(40 ,40);
std::copy(gray_sub_view.begin(),gray_sub_view.end(),iplwrapgray2.begin());
cvShowImage( winName, iplwrapgray2.pIplImage );
std::cout << "Wait Key Input" << std::endl;
cvWaitKey(0);
 

 

 
gray8_interleaved_view_t gray_view2=interleaved_view(iplwrapgray2.width() ,iplwrapgray2.height(),
gray8_interleaved_ptr(
reinterpret_cast(iplwrapgray2.pIplImage->imageData)
),
sizeof(unsigned char)* iplwrapgray2.pIplImage->widthStep);
std::copy(gray_sub_view.begin(),gray_sub_view.end(),gray_view2.begin());
cvShowImage( winName, iplwrapgray2.pIplImage );
std::cout << "Wait Key Input" << std::endl;
cvWaitKey(0);
 

 

cvReleaseImage( &piplimg );
 
#endif
 

piplimg=cvLoadImage( "test.jpg");
cvShowImage( winName, piplimg );
std::cout << "Wait Key Input" << std::endl;
cvWaitKey(0);
 
typedef interleaved_ptr bgr8_interleaved_ptr;
typedef type_from_x_iterator::view_t bgr8_interleaved_view_t;
 
bgr8_interleaved_view_t bgr_view=interleaved_view(piplimg->widthStep/3 ,piplimg->height,
bgr8_interleaved_ptr(
reinterpret_cast(piplimg->imageData)
),
sizeof(unsigned char)*piplimg->widthStep
);

 

 
iplimage_wrapper iplwrapbgr1(piplimg->widthStep/3 ,piplimg->height);
std::copy(bgr_view.begin(),bgr_view.end(),iplwrapbgr1.begin());
cvShowImage( winName, iplwrapbgr1.pIplImage );
std::cout << "Wait Key Input" << std::endl;
cvWaitKey(0);
 
x_gradient(bgr_view,((bgr8_interleaved_view_t)iplwrapbgr1));
cvShowImage( winName, iplwrapbgr1.pIplImage );
std::cout << "Wait Key Input" << std::endl;
cvWaitKey(0);
 

bgr8_interleaved_view_t bgr_sub_view=subimage_view(bgr_view, 20,30, 40,40);
 

iplimage_wrapper iplwrapbgr2(40 ,40);
std::copy(bgr_sub_view.begin(),bgr_sub_view.end(),iplwrapbgr2.begin());
cvShowImage( winName, iplwrapbgr2.pIplImage );
std::cout << "Wait Key Input" << std::endl;
cvWaitKey(0);
 

cvReleaseImage( &piplimg );

 
return 0;
}
 


GeneralRe: OpenCV wraped by adobe::gilmemberHirotaka Niitsuma3 Nov '06 - 14:35 
and plz add
---------------------------------
/************************************************************************/
/* */
/* Copyright 2006-2007 by Hirotaka Niitsuma */
/* */
/* */
/* This file is part of the STLLCV computer vision library. */
/* ( Version 0.7, 2006-10-31 ) */
/* You may use, modify, and distribute this software according */
/* to the terms stated in the LICENSE file included in */
/* the STLLCV distribution. */
/* */
/* The STLLCV Website is */
/* http://www2s.biglobe.ne.jp/~niitsuma/STLLCV/ */
/* Please direct questions, bug reports, and contributions to */
/* niitsuma@mub.biglobe.ne.jp    */
/* */
/* THIS SOFTWARE IS PROVIDED AS IS AND WITHOUT ANY EXPRESS OR */
/* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED */
/* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */
/* */
/************************************************************************/
GeneralBoost community will accept GIL instead of vigramemberHirotaka Niitsuma31 Oct '06 - 16:35 
Boost community will accept GIL( http://opensource.adobe.com/gil/ ) instead of vigra.
http://lists.boost.org/Archives/boost/2006/10/112316.php
>Having said that, I must say that I like Vigra a lot but I won't reject GIL
>on the ground of the elements in Vigra which can be considered to be better
>or which are missing in GIL.
>However, I do think that if GIL is accepted, it would be invaluable if
>Ullrich could bring in all his expertise and experience into the GIL team,

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130523.1 | Last Updated 17 Nov 2006
Article Copyright 2005 by Hirotaka Niitsuma
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid