Update of /cvsroot/hugin/hugin/src/hugin In directory sc8-pr-cvs5.sourceforge.net:/tmp/cvs-serv22038/src/hugin Modified Files: ImageCache.cpp AssistantPanel.cpp CropPanel.cpp ImagesPanel.cpp OptimizePhotometricPanel.cpp MainFrame.cpp wxPanoCommand.cpp Log Message: Use reference counted pointers for images in image cache. Should avoid solve all dangling image pointer problems. Index: MainFrame.cpp =================================================================== RCS file: /cvsroot/hugin/hugin/src/hugin/MainFrame.cpp,v retrieving revision 1.197 retrieving revision 1.198 diff -u -d -r1.197 -r1.198 --- MainFrame.cpp 15 Apr 2007 20:25:05 -0000 1.197 +++ MainFrame.cpp 8 May 2007 22:55:04 -0000 1.198 @@ -1172,6 +1172,8 @@ pdisp.pushTask(ProgressTask((const char *)wxString(_("Finetuning")).mb_str(),"",1.0/unoptimized.size())); + ImageCache & imgCache = ImageCache::getInstance(); + // do not process the control points in random order, // but walk from image to image, to reduce image reloading // in low mem situations. @@ -1184,17 +1186,11 @@ // finetune only normal points DEBUG_DEBUG("fine tuning point: " << *it); wxImage wxSearchImg; - ImageCache::getInstance().getImageWX( pano.getImage(cps[*it].image2Nr).getFilename(), wxSearchImg); - - wxImage wxTmplImg; - ImageCache::getInstance().getImageWX(pano.getImage(cps[*it].image1Nr).getFilename(), wxTmplImg); + ImageCache::EntryPtr searchImg = imgCache.getImage( + pano.getImage(cps[*it].image2Nr).getFilename(), true); - vigra::BasicImageView<vigra::RGBValue<unsigned char> > searchImg((vigra::RGBValue<unsigned char> *)wxSearchImg.GetData(), - wxSearchImg.GetWidth(), - wxSearchImg.GetHeight()); - vigra::BasicImageView<vigra::RGBValue<unsigned char> > templImg((vigra::RGBValue<unsigned char> *)wxTmplImg.GetData(), - wxTmplImg.GetWidth(), - wxTmplImg.GetHeight()); + ImageCache::EntryPtr templImg = imgCache.getImage( + pano.getImage(cps[*it].image1Nr).getFilename(), true); // load parameters long templWidth = wxConfigBase::Get()->Read( @@ -1207,10 +1203,10 @@ if (rotatingFinetune) { res = vigra_ext::PointFineTuneRotSearch( - templImg, + *(templImg->image8), roundP1, templWidth, - searchImg, + *(searchImg->image8), roundP2, sWidth, startAngle, stopAngle, nSteps @@ -1218,10 +1214,10 @@ } else { res = vigra_ext::PointFineTune( - templImg, + *(templImg->image8), roundP1, templWidth, - searchImg, + *(searchImg->image8), roundP2, sWidth ); Index: OptimizePhotometricPanel.cpp =================================================================== RCS file: /cvsroot/hugin/hugin/src/hugin/OptimizePhotometricPanel.cpp,v retrieving revision 1.2 retrieving revision 1.3 diff -u -d -r1.2 -r1.3 --- OptimizePhotometricPanel.cpp 17 Apr 2007 07:26:11 -0000 1.2 +++ OptimizePhotometricPanel.cpp 8 May 2007 22:55:04 -0000 1.3 @@ -455,7 +455,7 @@ // get the small images std::vector<vigra::FRGBImage *> srcImgs; for (size_t i=0; i < optPano.getNrOfImages(); i++) { - ImageCache::Entry * e = ImageCache::getInstance().getSmallImage(optPano.getImage(i).getFilename()); + ImageCache::EntryPtr e = ImageCache::getInstance().getSmallImage(optPano.getImage(i).getFilename()); vigra::FRGBImage * img = new FRGBImage; if (!e) { wxMessageBox(_("Error: could not load all images"), _("Error")); Index: wxPanoCommand.cpp =================================================================== RCS file: /cvsroot/hugin/hugin/src/hugin/wxPanoCommand.cpp,v retrieving revision 1.8 retrieving revision 1.9 diff -u -d -r1.8 -r1.9 --- wxPanoCommand.cpp 15 Apr 2007 20:51:02 -0000 1.8 +++ wxPanoCommand.cpp 8 May 2007 22:55:04 -0000 1.9 @@ -48,13 +48,23 @@ { PanoCommand::execute(); - + // TODO: rewrite, and use same function for modular image creation. +#if 1 const PanoImage & i1 = pano.getImage(img1); const PanoImage & i2 = pano.getImage(img1); // run both images through the harris corner detector - const vigra::BImage & leftImg = ImageCache::getInstance().getPyramidImage( - i1.getFilename(),1); + ImageCache::EntryPtr eptr = ImageCache::getInstance().getSmallImage(i1.getFilename(), true); + vigra::BImage leftImg(eptr->image8->size()); + + vigra::GreenAccessor<vigra::RGBValue<vigra::UInt8> > ga; + vigra::copyImage(srcImageRange(*(eptr->image8), ga ), + destImage(leftImg)); + + double scale = i1.getWidth() / (double) leftImg.width(); + + //const vigra::BImage & leftImg = ImageCache::getInstance().getPyramidImage( + // i1.getFilename(),1); BImage leftCorners(leftImg.size()); FImage leftCornerResponse(leftImg.size()); @@ -104,23 +114,25 @@ int border = 5; double sphx, sphy; double img2x, img2y; + // need to scale the images. // sample grid on img1 and try to add ctrl points - for (unsigned int x=0; x < i1.getWidth(); x += 2 ) { - for (unsigned int y=0; y < i1.getHeight(); y +=2 ) { - if (leftCorners(x/2,y/2) > 0) { - img1ToSphere.transformImgCoord(sphx, sphy, x, y); + for (unsigned int x=0; x < leftImg.width(); x++ ) { + for (unsigned int y=0; y < leftImg.height(); y++) { + if (leftCorners(x,y) > 0) { + img1ToSphere.transformImgCoord(sphx, sphy, scale*x, scale*y); sphereToImg2.transformImgCoord(img2x, img2y, sphx, sphy); // check if it is inside.. if ( img2x > border && img2x < i2.getWidth() - border && img2y > border && img2y < i2.getHeight() - border ) { // add control point - ControlPoint p(img1,x, y, img2, img2x, img2y); + ControlPoint p(img1, scale*x, scale*y, img2, img2x, img2y); pano.addCtrlPoint(p); } } } } +#endif pano.changeFinished(); } Index: AssistantPanel.cpp =================================================================== RCS file: /cvsroot/hugin/hugin/src/hugin/AssistantPanel.cpp,v retrieving revision 1.14 retrieving revision 1.15 diff -u -d -r1.14 -r1.15 --- AssistantPanel.cpp 15 Apr 2007 20:35:02 -0000 1.14 +++ AssistantPanel.cpp 8 May 2007 22:55:04 -0000 1.15 @@ -516,7 +516,7 @@ // get the small images std::vector<vigra::FRGBImage *> srcImgs; for (size_t i=0; i < optPano.getNrOfImages(); i++) { - ImageCache::Entry * e = ImageCache::getInstance().getSmallImage(optPano.getImage(i).getFilename()); + ImageCache::EntryPtr e = ImageCache::getInstance().getSmallImage(optPano.getImage(i).getFilename()); vigra::FRGBImage * img = new FRGBImage; if (!e) { wxMessageBox(_("Error: could not load all images"), _("Error")); Index: CropPanel.cpp =================================================================== RCS file: /cvsroot/hugin/hugin/src/hugin/CropPanel.cpp,v retrieving revision 1.4 retrieving revision 1.5 diff -u -d -r1.4 -r1.5 --- CropPanel.cpp 15 Apr 2007 20:51:02 -0000 1.4 +++ CropPanel.cpp 8 May 2007 22:55:04 -0000 1.5 @@ -178,9 +178,9 @@ std::string newImgFile = img.getFilename(); // check if we need to display a new image if (m_currentImageFile != newImgFile) { - wxImage wximg; - ImageCache::getInstance().getImageWX(newImgFile, wximg); - m_Canvas->SetImage(wximg); +// wxImage wximg; + ImageCache::EntryPtr imgV = ImageCache::getInstance().getImage(newImgFile, true); + m_Canvas->SetImage(imgV); m_currentImageFile == newImgFile; } m_imgOpts = img.getOptions(); @@ -532,9 +532,10 @@ } } -void CenterCanvas::SetImage(wxImage & s_img) +void CenterCanvas::SetImage(ImageCache::EntryPtr s_img) { - img = s_img; + m_imgCacheImg = s_img; + img = imageCacheEntry2wxImage(s_img); wxSizeEvent e; Resize (e); Index: ImageCache.cpp =================================================================== RCS file: /cvsroot/hugin/hugin/src/hugin/ImageCache.cpp,v retrieving revision 1.56 retrieving revision 1.57 diff -u -d -r1.56 -r1.57 --- ImageCache.cpp 17 Apr 2007 05:33:43 -0000 1.56 +++ ImageCache.cpp 8 May 2007 22:55:04 -0000 1.57 @@ -35,6 +35,7 @@ #include <vigra/basicimageview.hxx> #include <vigra/rgbvalue.hxx> #include <vigra/impex.hxx> +#include <vigra/error.hxx> #include <vigra_ext/utils.h> #include <vigra_ext/impexalpha.hxx> #include <vigra_ext/Pyramid.h> @@ -108,16 +109,14 @@ void ImageCache::removeImage(const std::string & filename) { - map<string, Entry*>::iterator it = images.find(filename); + map<string, EntryPtr>::iterator it = images.find(filename); if (it != images.end()) { - delete it->second; images.erase(it); } string sfilename = filename + string("_small"); it = images.find(sfilename); if (it != images.end()) { - delete it->second; images.erase(it); } @@ -139,12 +138,6 @@ void ImageCache::flush() { - for (map<string, Entry*>::iterator it = images.begin(); - it != images.end(); - ++it) - { - delete it->second; - } images.clear(); for (map<string, vigra::BImage*>::iterator it = pyrImages.begin(); @@ -164,7 +157,7 @@ // calculate used memory long imgMem = 0; - std::map<std::string, Entry*>::iterator imgIt; + std::map<std::string, EntryPtr>::iterator imgIt; for(imgIt=images.begin(); imgIt != images.end(); imgIt++) { if (imgIt->second->image8) { imgMem += imgIt->second->image8->width() * imgIt->second->image8->height() * 3; @@ -196,7 +189,7 @@ // use least recently uses strategy. // sort images by their access time std::map<int,std::string> accessMap; - for (map<string, Entry*>::iterator it = images.begin(); + for (map<string, EntryPtr>::iterator it = images.begin(); it != images.end(); ++it) { @@ -215,7 +208,7 @@ deleted = true; } else if (accessMap.size() > 0) { std::map<int,std::string>::iterator accIt = accessMap.begin(); - map<string, Entry*>::iterator it = images.find(accIt->second); + map<string, EntryPtr>::iterator it = images.find(accIt->second); if (it != images.end()) { DEBUG_DEBUG("soft flush: removing image: " << it->first); if (it->second->image8) { @@ -224,7 +217,6 @@ if (it->second->imageFloat) { purgedMem += it->second->imageFloat->width() * it->second->imageFloat->height()*3*4; } - delete it->second; images.erase(it); accessMap.erase(accIt); deleted = true; @@ -313,17 +305,12 @@ #endif -void convertTo8Bit(ImageCache::Entry * entry, wxImage & img) +void convertTo8Bit(vigra::FRGBImage & src, std::string origType, vigra::BRGBImage & dest) { - DEBUG_ASSERT(entry->imageFloat); - // code to apply the mapping to 8 bit // always scaled from 0..1 for integer images. - img = wxImage(entry->imageFloat->size().x, entry->imageFloat->size().x, false); - BasicImageView<RGBValue<unsigned char> > imgview((RGBValue<unsigned char> *)img.GetData(), - img.GetWidth(), - img.GetHeight()); + dest.resize(src.size()); double min=0; double max=1; @@ -331,17 +318,17 @@ int mapping = wxConfigBase::Get()->Read(wxT("/ImageCache/MappingInteger"), HUGIN_IMGCACHE_MAPPING_INTEGER); // float needs to be from min ... max. - if (entry->origType == "FLOAT" || entry->origType == "DOUBLE") + if (origType == "FLOAT" || origType == "DOUBLE") { vigra::RGBToGrayAccessor<RGBValue<float> > ga; vigra::FindMinMax<float> minmax; // init functor - vigra::inspectImage(srcImageRange(*(entry->imageFloat), ga), + vigra::inspectImage(srcImageRange(src, ga), minmax); min = minmax.min; max = minmax.max; mapping = wxConfigBase::Get()->Read(wxT("/ImageCache/MappingFloat"), HUGIN_IMGCACHE_MAPPING_FLOAT); } - applyMapping(srcImageRange(*(entry->imageFloat)), destImage(imgview), min, max, mapping); + applyMapping(srcImageRange(src), destImage(dest), min, max, mapping); } @@ -490,15 +477,23 @@ } } -ImageCache::Entry* ImageCache::getImage(const std::string & filename) +ImageCache::EntryPtr ImageCache::getImage(const std::string & filename, + bool integer) { // softFlush(); - std::map<std::string, Entry *>::iterator it; + std::map<std::string, EntryPtr>::iterator it; it = images.find(filename); if (it != images.end()) { m_accessCounter++; it->second->lastAccess = m_accessCounter; + if (integer && it->second->image8->width() == 0) { + // convert to 8 bit + it->second->image8->resize(it->second->imageFloat->size()); + convertTo8Bit(*(it->second->imageFloat), + it->second->origType, + *(it->second->image8)); + } return it->second; } else { if (m_progress) { @@ -508,20 +503,13 @@ #if 1 // load images with VIGRA impex, and store either 8 bit or float images std::string pixelTypeStr; - vigra::BRGBImage * img8 = 0; - vigra::FRGBImage * imgFloat = 0; - vigra::BImage * mask = 0; + ImageCacheRGB8Ptr img8(new vigra::BRGBImage); + ImageCacheRGBFloatPtr imgFloat(new vigra::FRGBImage); + ImageCache8Ptr mask(new vigra::BImage); + try { ImageImportInfo info(filename.c_str()); - /* - image = new wxImage(info.width(), info.height()); - - BasicImageView<RGBValue<unsigned char> > imgview((RGBValue<unsigned char> *)image->GetData(), - image->GetWidth(), - image->GetHeight()); - */ - int bands = info.numBands(); int extraBands = info.numExtraBands(); const char * pixelType = info.getPixelType(); @@ -530,9 +518,9 @@ DEBUG_DEBUG(filename << ": bands: " << bands << " extra bands: " << extraBands << " type: " << pixelType); if (pixelTypeStr == "UINT8") { - img8 = new vigra::BRGBImage(info.size()); + img8->resize(info.size()); } else { - imgFloat = new vigra::FRGBImage(info.size()); + imgFloat->resize(info.size()); } wxString pixelTypeWX(pixelType, *wxConvCurrent); @@ -594,7 +582,6 @@ } } else if ( bands == 4 && extraBands == 1) { // load and convert image to 8 bit, if needed - mask = new vigra::BImage(info.size()); if (strcmp(pixelType, "UINT8") == 0 ) { vigra::importImageAlpha(info, destImage(*img8), destImage(*mask)); } else if (strcmp(pixelType, "INT16") == 0 ) { @@ -647,7 +634,7 @@ } catch (std::exception & e) { // could not load image.. wxLogError(wxString::Format(_("Error during image reading: %s"), wxString(e.what(),*wxConvCurrent).c_str())); - return 0; + throw; } #else @@ -661,35 +648,22 @@ if (m_progress) { m_progress->popTask(); } - Entry * e = new Entry(img8, imgFloat, pixelTypeStr); - images[filename] = e; - return e; - } -} -ImageCache::Entry * ImageCache::getImageWX(const std::string & filename, wxImage & img) -{ - Entry * e = getImage(filename); - if (e == 0) - return 0; - if (e->image8) { - // 8 bit image. - img = wxImage(e->image8->width(), e->image8->height(), (unsigned char*) &(e->image8->operator()(0,0)), true); - return e; - } else if (e->imageFloat) { - // convert to 8 bit - convertTo8Bit(e, img); + if (integer && img8->width() == 0) { + // convert to 8 bit + convertTo8Bit(*imgFloat, pixelTypeStr, *img8); + } + EntryPtr e(new Entry(img8, imgFloat, mask, pixelTypeStr)); + images[filename] = e; return e; - } else { - return 0; } } - -ImageCache::Entry * ImageCache::getSmallImage(const std::string & filename) +ImageCache::EntryPtr ImageCache::getSmallImage(const std::string & filename, + bool integer) { softFlush(); - std::map<std::string, Entry*>::iterator it; + std::map<std::string, EntryPtr>::iterator it; // "_small" is only used internally string name = filename + string(":small"); it = images.find(name); @@ -701,83 +675,60 @@ m_progress->pushTask(ProgressTask((const char *)wxString::Format(_("Scaling image %s"),wxString(utils::stripPath(filename).c_str(), *wxConvCurrent).c_str()).mb_str(), "", 0)); } DEBUG_DEBUG("creating small image " << name ); - Entry * entry = getImage(filename); - if (entry) { - // && entry->image8 - size_t w=0; - size_t h=0; - if (entry->image8) { - w = entry->image8->width(); - h = entry->image8->height(); - } else if (entry->imageFloat) { - w = entry->imageFloat->width(); - h = entry->imageFloat->height(); - } else { - return 0; - } + EntryPtr entry = getImage(filename); + // && entry->image8 + size_t w=0; + size_t h=0; + if (entry->image8) { + w = entry->image8->width(); + h = entry->image8->height(); + } else if (entry->imageFloat) { + w = entry->imageFloat->width(); + h = entry->imageFloat->height(); + } else { + vigra_fail("Could not load image"); + } - size_t sz = w*h; - size_t smallImageSize = wxConfigBase::Get()->Read(wxT("/ImageCache/SmallImageSize"), 500 * 500l); + size_t sz = w*h; + size_t smallImageSize = wxConfigBase::Get()->Read(wxT("/ImageCache/SmallImageSize"), 800 * 800l); - int nLevel=0; - while(sz > smallImageSize) { - sz /=4; - nLevel++; - } - Entry * e = new Entry(0, 0, entry->origType); - if (entry->image8) { - e->image8 = new BRGBImage; - if (entry->mask) { - e->mask = new BImage; - reduceNTimes(*(entry->image8), *(entry->mask), *(e->image8), *(e->mask), nLevel); - } else { - reduceNTimes(*(entry->image8), *(e->image8), nLevel); - } + int nLevel=0; + while(sz > smallImageSize) { + sz /=4; + nLevel++; + } + EntryPtr e(new Entry); + e->origType = entry->origType; + if (entry->imageFloat->width() != 0 ) { + e->imageFloat = ImageCacheRGBFloatPtr(new FRGBImage); + if (entry->mask->width() != 0) { + reduceNTimes(*(entry->imageFloat), *(entry->mask), *(e->imageFloat), *(e->mask), nLevel); } else { - e->imageFloat = new FRGBImage; - if (entry->mask) { - e->mask = new BImage; - reduceNTimes(*(entry->imageFloat), *(entry->mask), *(e->imageFloat), *(e->mask), nLevel); - } else { - reduceNTimes(*(entry->imageFloat), *(e->imageFloat), nLevel); - } - } - images[name] = e; - DEBUG_INFO ( "created small image: " << name); - if (m_progress) { - m_progress->popTask(); + reduceNTimes(*(entry->imageFloat), *(e->imageFloat), nLevel); } - return e; - } else { - if (m_progress) { - m_progress->popTask(); + } + if (entry->image8->width() != 0) { + e->image8 = ImageCacheRGB8Ptr(new vigra::BRGBImage); + if ((entry->mask->width() != 0) && (e->mask->width() == 0) ) { + reduceNTimes(*(entry->image8), *(entry->mask), *(e->image8), *(e->mask), nLevel); + } else { + reduceNTimes(*(entry->image8), *(e->image8), nLevel); } - return 0; } - } -} - -ImageCache::Entry * ImageCache::getSmallImageWX(const std::string & filename, wxImage & img) -{ - Entry * e = getSmallImage(filename); - if (e == 0) - return 0; - if (e->image8) { - // 8 bit image. - vigra_precondition(e->image8->width()> 0 && e->image8->height() > 0, "8 bit small image has zero size"); - img = wxImage(e->image8->width(), e->image8->height(), (unsigned char*) &(e->image8->operator()(0,0)), true); - return e; - } else if (e->imageFloat) { - // convert to 8 bit - vigra_precondition(e->imageFloat->width()> 0 && e->imageFloat->height() > 0, "float small image has zero size"); - convertTo8Bit(e, img); + if (integer && e->image8->width() == 0) { + // convert to 8 bit + convertTo8Bit(*(e->imageFloat), e->origType, *(e->image8)); + } + images[name] = e; + DEBUG_INFO ( "created small image: " << name); + if (m_progress) { + m_progress->popTask(); + } return e; - } else { - return 0; } } - +#if 0 const vigra::BImage & ImageCache::getPyramidImage(const std::string & filename, int level) { @@ -805,7 +756,7 @@ if (key.level == 0) { // special case, create first gray image wxImage srcImg; - Entry * e = getImageWX(filename, srcImg); + EntryPtr e = getImageWX(filename, srcImg); if (e == 0) { vigra_fail("Error loading initial pyramid image"); } @@ -847,6 +798,9 @@ } } +#endif + + SmallRemappedImageCache::~SmallRemappedImageCache() { invalidate(); @@ -894,12 +848,12 @@ const PanoImage & img = pano.getImage(imgNr); const PT::ImageOptions & iopts = img.getOptions(); - ImageCache::Entry * e = ImageCache::getInstance().getSmallImage(img.getFilename().c_str()); - if (!e) { + ImageCache::EntryPtr e = ImageCache::getInstance().getSmallImage(img.getFilename().c_str()); + if ( (e->image8->width() == 0) && (e->imageFloat->width() == 0) ) { throw std::runtime_error("could not retrieve small source image for preview generation"); } Size2D srcImgSize; - if (e->image8) + if (e->image8->width() > 0) srcImgSize = e->image8->size(); else srcImgSize = e->imageFloat->size(); @@ -914,11 +868,11 @@ BImage srcMask; if (iopts.m_vigCorrMode & ImageOptions::VIGCORR_FLATFIELD) { - ImageCache::Entry * e = ImageCache::getInstance().getSmallImage(iopts.m_flatfield.c_str()); + ImageCache::EntryPtr e = ImageCache::getInstance().getSmallImage(iopts.m_flatfield.c_str()); if (!e) { throw std::runtime_error("could not retrieve flatfield image for preview generation"); } - if (e->image8) { + if (e->image8->width()) { srcFlat.resize(e->image8->size()); vigra::copyImage(srcImageRange(*(e->image8), RGBToGrayAccessor<RGBValue<vigra::UInt8> >()), @@ -932,7 +886,7 @@ } progress.pushTask(ProgressTask("remapping", "", 0)); - if (e->imageFloat) { + if (e->imageFloat->width()) { // remap image remapImage(*(e->imageFloat), srcMask, @@ -983,3 +937,17 @@ } } +wxImage imageCacheEntry2wxImage(ImageCache::EntryPtr e) +{ + if (e->image8->width() > 0) { + return wxImage(e->image8->width(), + e->image8->height(), + (unsigned char *)e->image8->data(), + true); + } else { + // invalid wxImage + return wxImage(); + } + +} + Index: ImagesPanel.cpp =================================================================== RCS file: /cvsroot/hugin/hugin/src/hugin/ImagesPanel.cpp,v retrieving revision 1.103 retrieving revision 1.104 diff -u -d -r1.103 -r1.104 --- ImagesPanel.cpp 15 Apr 2007 20:51:02 -0000 1.103 +++ ImagesPanel.cpp 8 May 2007 22:55:04 -0000 1.104 @@ -507,14 +507,15 @@ if (m_showImgNr < 0 || m_showImgNr >= pano.getNrOfImages()) { return; } - wxImage img; - ImageCache::Entry * cacheEntry = ImageCache::getInstance().getSmallImageWX( - pano.getImage(m_showImgNr).getFilename(), img); - if (! cacheEntry) { + ImageCache::EntryPtr cacheEntry = ImageCache::getInstance().getSmallImage( + pano.getImage(m_showImgNr).getFilename(), true); + // TODO: catch assertation + if (cacheEntry->image8->width() == 0) { return; } + wxImage img = imageCacheEntry2wxImage(cacheEntry); - double iRatio = (double)img.GetWidth() / img.GetHeight(); + double iRatio = cacheEntry->image8->width() / (double) cacheEntry->image8->height(); wxSize sz; #ifdef USE_WX253 |