/******************************************************************** ** Image Component Library (ICL) ** ** ** ** Copyright (C) 2006-2013 CITEC, University of Bielefeld ** ** Neuroinformatics Group ** ** Website: www.iclcv.org and ** ** http://opensource.cit-ec.de/projects/icl ** ** ** ** File : ICLFilter/src/ICLFilter/LocalThresholdOp.cpp ** ** Module : ICLFilter ** ** Authors: Christof Elbrechter ** ** ** ** ** ** GNU LESSER GENERAL PUBLIC LICENSE ** ** This file may be used under the terms of the GNU Lesser General ** ** Public License version 3.0 as published by the ** ** ** ** Free Software Foundation and appearing in the file LICENSE.LGPL ** ** included in the packaging of this file. Please review the ** ** following information to ensure the license requirements will ** ** be met: http://www.gnu.org/licenses/lgpl-3.0.txt ** ** ** ** The development of this software was supported by the ** ** Excellence Cluster EXC 277 Cognitive Interaction Technology. ** ** The Excellence Cluster EXC 277 is a grant of the Deutsche ** ** Forschungsgemeinschaft (DFG) in the context of the German ** ** Excellence Initiative. ** ** ** ********************************************************************/ #include #include #include #include #include #include #include #include #include #include #include template inline unsigned char myclip(T x){ return x < 0.0 ? 0 : x > 255.0 ? 255 : x; } template inline void ppm_write(const icl::core::Img &image, const std::string &filename){ FILE *fp = fopen(filename.c_str(), "wb"); /* b - binary mode */ (void) fprintf(fp, "P6\n%d %d\n255\n", image.getWidth(),image.getHeight()); for (int j = 0; j < image.getHeight(); ++j){ for (int i = 0; i < image.getWidth(); ++i){ static unsigned char color[3]; if(image.getChannels() == 3){ color[0] = myclip(image(i,j,0)); color[1] = myclip(image(i,j,1)); color[2] = myclip(image(i,j,2)); (void) fwrite(color, 1, 3, fp); }else if(image.getChannels() == 1){ color[0] = color[1] = color[2] = myclip(image(i,j,0)); (void) fwrite(color, 1, 3, fp); } } } (void) fclose(fp); } using namespace icl::utils; using namespace icl::core; namespace icl{ namespace filter{ LocalThresholdOp::LocalThresholdOp(unsigned int maskSize, float globalThreshold, float gammaSlope): // {{{ open m_roiBufSrc(0), m_roiBufDst(0), m_iiOp(new IntegralImgOp), m_cmp(new BinaryCompareOp(BinaryCompareOp::gt)), m_tiledBuf1(0),m_tiledBuf2(0){ /* - mask size (range:spinbox) - global threshold (range:slider -100000,100000) - gamma slope (range:slider(-10,10) - algorithm (menu, region mean, tiled lin, tiled NN) */ addProperty("mask size","range:slider","[1,100]:1",str(maskSize)); addProperty("global threshold","range:slider","[-255,255]",str(globalThreshold)); addProperty("gamma slope","range:slider","[-255,255]",str(gammaSlope)); addProperty("algorithm","menu","region mean,tiled linear,tiled NN,global","region mean"); addProperty("actually used mask size","info","","0"); addProperty("invert output","flag","",false); } // }}} LocalThresholdOp::LocalThresholdOp(LocalThresholdOp::algorithm a, int maskSize, float globalThreshold, float gammaSlope): // {{{ open m_roiBufSrc(0), m_roiBufDst(0), m_iiOp(new IntegralImgOp), m_cmp(new BinaryCompareOp(BinaryCompareOp::gt)), m_tiledBuf1(0),m_tiledBuf2(0){ addProperty("mask size","range:slider","[1,100]",str(maskSize)); addProperty("global threshold","range:slider","[-255,255]",str(globalThreshold)); addProperty("gamma slope","range:slider","[-10,10]",str(gammaSlope)); addProperty("algorithm","menu","region mean,tiled linear,tiled NN,gobal",a==regionMean?"region mean":a==tiledNN?"tiled NN":"tiled linear"); addProperty("actually used mask size","info","","0"); } // }}} LocalThresholdOp::~LocalThresholdOp(){ // {{{ open ICL_DELETE(m_roiBufDst); ICL_DELETE(m_iiOp); ICL_DELETE(m_cmp); ICL_DELETE(m_tiledBuf1); ICL_DELETE(m_tiledBuf2); } // }}} void LocalThresholdOp::setMaskSize(unsigned int maskSize){ // {{{ open prop("mask size").value = str(maskSize); call_callbacks("mask size",this); } // }}} void LocalThresholdOp::setGlobalThreshold(float globalThreshold){ // {{{ open prop("global threshold").value = str(globalThreshold); call_callbacks("global threshold",this); } // }}} void LocalThresholdOp::setGammaSlope(float gammaSlope){ // {{{ open prop("gamma slope").value = str(gammaSlope); call_callbacks("gamma slope",this); } // }}} unsigned int LocalThresholdOp::getMaskSize() const{ // {{{ open return parse(prop("mask size").value); } // }}} float LocalThresholdOp::getGlobalThreshold() const{ // {{{ open return parse(prop("global threshold").value); } // }}} float LocalThresholdOp::getGammaSlope() const{ // {{{ open return parse(prop("gamma slope").value); } // }}} void LocalThresholdOp::setup(unsigned int maskSize, float globalThreshold, LocalThresholdOp::algorithm a, float gammaSlope){ // {{{ open setMaskSize(maskSize); setGlobalThreshold(globalThreshold); setGammaSlope(gammaSlope); setAlgorithm(a); } // }}} /// returns currently used algorithm type LocalThresholdOp::algorithm LocalThresholdOp::getAlgorithm() const{ // {{{ open const std::string &a = prop("algorithm").value; return ( a == "region mean" ? regionMean : a =="tiled NN" ? tiledNN : a == "global" ? global : tiledLIN ); } // }}} /// sets internally used algorithm void LocalThresholdOp::setAlgorithm(algorithm a){ // {{{ open prop("algorithm").value = (a==regionMean?"region mean":a==tiledNN?"tiledNN": a == global ? "global" : "tiled linear"); call_callbacks("algorithm",this); } // }}} /// this template resolves the destination images depths and if a gamma slope is set or not template void apply_local_threshold_six(const Img &src,const Img &ii, ImgBase *dst, float tf, int m, float gs){ // {{{ open typename ThreshType::T t = (typename ThreshType::T)(tf); int w = src.getWidth(), h = src.getHeight(); const S *psrc = src.begin(0); const I *pii = ii.begin(0); switch(dst->getDepth()){ case depth8u: if(gs!=0.0f){ for(int c=0;c::T,true>(psrc,pii,dst->asImg()->begin(0),w,h,m,t,gs,c); } }else{ for(int c=0;c::T,false>(psrc,pii,dst->asImg()->begin(0),w,h,m,t,gs,c); } } break; case depth16s: if(gs!=0.0f){ for(int c=0;c::T,true>(psrc,pii,dst->asImg()->begin(0),w,h,m,t,gs,c); } }else{ for(int c=0;c::T,false>(psrc,pii,dst->asImg()->begin(0),w,h,m,t,gs,c); } } break; case depth32s: if(gs!=0.0f){ for(int c=0;c::T,true>(psrc,pii,dst->asImg()->begin(0),w,h,m,t,gs,c); } }else{ for(int c=0;c::T,false>(psrc,pii,dst->asImg()->begin(0),w,h,m,t,gs,c); } } break; case depth32f: if(gs!=0.0f){ for(int c=0;c::T,true>(psrc,pii,dst->asImg()->begin(0),w,h,m,t,gs,c); } }else{ for(int c=0;c::T,false>(psrc,pii,dst->asImg()->begin(0),w,h,m,t,gs,c); } } break; case depth64f: if(gs!=0.0f){ for(int c=0;c::T,true>(psrc,pii,dst->asImg()->begin(0),w,h,m,t,gs,c); } }else{ for(int c=0;c::T,false>(psrc,pii,dst->asImg()->begin(0),w,h,m,t,gs,c); } } break; default: // this may not happen ICL_INVALID_DEPTH; } } // }}} /// this template resolves the integral image depths template void apply_local_threshold_sxx(const Img &src,const ImgBase *ii, ImgBase *dst, float t,unsigned int m, float gs){ // {{{ open switch(ii->getDepth()){ case depth32s: apply_local_threshold_six(src,*ii->asImg(),dst,t,m,gs); break; case depth32f: apply_local_threshold_six(src,*ii->asImg(),dst,t,m,gs); break; case depth64f: apply_local_threshold_six(src,*ii->asImg(),dst,t,m,gs); break; default: // this may not happen ICL_INVALID_DEPTH; } } // }}} template void LocalThresholdOp::apply_a(const ImgBase*, ImgBase**){ // {{{ open throw ICLException("this algorithm is not yet implemented for the LocalThresholdOp class"); } // }}} template inline T roi_mean_gen(const Channel &s, int dim, const Rect &roi){ B buf = 0; for(int y=roi.y; y< roi.bottom();++y){ for(int x=roi.x; x< roi.right();++x){ buf += s(x,y); } } return buf/dim; } template inline T roi_mean(const Channel &s, int dim, const Rect &roi){ return roi_mean_gen(s,dim,roi); } template<> inline icl8u roi_mean(const Channel &s, int dim, const Rect &roi){ return roi_mean_gen(s,dim,roi); } template<> inline icl16s roi_mean(const Channel &s, int dim, const Rect &roi){ return roi_mean_gen(s,dim,roi); } template<> inline icl32s roi_mean(const Channel &s, int dim, const Rect &roi){ return roi_mean_gen(s,dim,roi); } template static void roi_cmp(const Channel &s, const Rect &r, S val, Channel8u &d, int threshold){ const int maxy = r.bottom(); for(int y=r.y;y (val + threshold)); } } } /// todo: fix-point approx for icl8u type template static void linear_interpolate(S a, S b, S *dst, int n){ // fixed-point approx x 100 float dy = ((float)b - float(a)); float slope = dy / float(n-1); for(int i=0;i static void linear_interpolate_cmp(S a, S b, int n, const S *src, icl8u *dst, int threshold, icl32f *cmp){ // fixed-point approx x 100 float dy = ((float)b - float(a)); float slope = dy / float(n-1); float base = a + threshold; for(int i=0;i (base + i * slope)); // cmp[i] = base + i * slope; } } template static void compare_lin(S *cL,S *cR, int tsx, int tsy, S ul, S ur, S ll, S lr, const Channel &src, Channel8u dst, const Rect &roi, int threshold, Channel32f cmp){ linear_interpolate(ul,ll,cL,tsy); linear_interpolate(ur,lr,cR,tsy); for(int y=0;y(cL[y], cR[y], tsx, &src(roi.x,roi.y+y), &dst(roi.x,roi.y+y), threshold, &cmp(roi.x,roi.y+y)); } } template static void apply_tiled_thresh(const Img &s, Img8u &dst, Img &buf1, Img &buf2, int ts, int threshold, BinaryCompareOp *cmp, bool lin){ int w = s.getWidth(); int h = s.getHeight(); Size t(ts,ts); int dim = ts*ts; int NX = w/ts; int NY = h/ts; Rect r(0,0,ts,ts); // Time tt2 = Time::now(); /* the old version is outdated now Benchmarks: 1280x960 single channel (scaled lena image) OLD with IPP: 13.5ms NEW (no IPP involved) 2.7ms */ #ifdef ICL_HAVE_IPP int bw = w/ts; // Time tt = tt2; for(int c=s.getChannels()-1;c>=0;--c){ S *pbuf1 = buf1.begin(c); const Channel chan = s[c]; for(int y=0;yapply(&s,&buf2,bpp(dst)); //tt.printAge("compare"); #else for(int c=s.getChannels()-1;c>=0;--c){ //S *pbuf1 = buf1.begin(c); const Channel srcChan = s[c]; Channel8u dstChan = dst[c]; if(lin){ Channel bufChan = buf1[0]; const Channel chan = s[c]; for(int y=0;y void LocalThresholdOp::apply_a(const ImgBase *src, ImgBase **dst){ // {{{ open int ts = 2*getMaskSize(); // find closes number that devides w and h int w = src->getWidth(), h = src->getHeight(); int max = iclMax(w,h); for(int i=0;i adapted:" << ts << std::endl; ICLASSERT_RETURN(ts>1); Size size = src->getSize(); ensureCompatible(&m_tiledBuf1,src->getDepth(),size/ts, 1, formatMatrix); ensureCompatible(&m_tiledBuf2,src->getDepth(),size,1,formatMatrix); switch(src->getDepth()){ #define ICL_INSTANTIATE_DEPTH(D) \ case depth##D: \ apply_tiled_thresh(*src->asImg(), \ *(*dst)->asImg(), \ *m_tiledBuf1->asImg(), \ *m_tiledBuf2->asImg(), \ ts, getGlobalThreshold(), m_cmp, \ getAlgorithm() == tiledLIN); \ break; ICL_INSTANTIATE_ALL_DEPTHS #undef ICL_INSTANTIATE_DEPTH } } // }}} template<> void LocalThresholdOp::apply_a(const ImgBase *src, ImgBase **dst){ // {{{ open apply_a(src,dst); // LIN vs NN is handled by a runtime-bool } // }}} template<> void LocalThresholdOp::apply_a(const ImgBase *src, ImgBase **dst){ // {{{ open m_iiOp->setIntegralImageDepth((src->getDepth() == depth8u || src->getDepth() == depth16s) ? depth32s : src->getDepth()); const ImgBase *ii = m_iiOp->apply(src); float t = getGlobalThreshold(); int s = getMaskSize(); setPropertyValue("actually used mask size",s); float gs = getGammaSlope(); switch(src->getDepth()){ #define ICL_INSTANTIATE_DEPTH(D) case depth##D: apply_local_threshold_sxx(*src->asImg(), ii, *dst, t, s, gs); break; ICL_INSTANTIATE_ALL_DEPTHS; #undef ICL_INSTANTIATE_DEPTH } } // }}} void LocalThresholdOp::apply(const ImgBase *src, ImgBase **dst){ // {{{ open ICLASSERT_RETURN( src ); ICLASSERT_RETURN( src->getSize() != Size::null ); ICLASSERT_RETURN( src->getChannels() ); ICLASSERT_RETURN( dst ); ICLASSERT_RETURN( src != *dst ); if(getAlgorithm() == global){ UnaryCompareOp cmp(">",getGlobalThreshold()); cmp.apply(src,dst); return; } const ImgBase *srcOrig = src; bool roi = false; // cut the roi of src if set if(!(src->hasFullROI())){ ensureCompatible(&m_roiBufSrc, src->getDepth(), src->getROISize(), src->getChannels(), src->getFormat()); src->deepCopyROI(&m_roiBufSrc); src = m_roiBufSrc; roi = true; } ICLASSERT_RETURN(src->getWidth() > 2*(int)getMaskSize()); ICLASSERT_RETURN(src->getHeight() > 2*(int)getMaskSize()); // prepare the destination image depth dstDepth = getAlgorithm() == regionMean ? (getGammaSlope() ? depth32f : depth8u) : depth8u; ImgBase **useDst = roi ? &m_roiBufDst : dst; if(!prepare(useDst, dstDepth, src->getSize(), formatMatrix, src->getChannels(), Rect::null)){ ERROR_LOG("prepare failure [code 1]"); return; } switch(getAlgorithm()){ case regionMean: apply_a(src,useDst); break; case tiledNN: apply_a(src,useDst); break; case tiledLIN: apply_a(src,useDst); break; default: throw ICLException(std::string(__FUNCTION__)+": invalid algorithm value"); } if(roi){ if(!prepare(dst, srcOrig, (*useDst)->getDepth())){ ERROR_LOG("prepare failure [code 2]"); return; } (*useDst)->deepCopyROI(dst); } (*dst)->setTime(src->getTime()); if(dstDepth == depth8u && getPropertyValue("invert output").as()){ Channel8u c = (*(*dst)->as8u())[0]; const int dim = c.getDim(); for(int i=0;i