Skip to content
Snippets Groups Projects
CImg.h 2.76 MiB
Newer Older
  • Learn to ignore specific revisions
  • 56001 56002 56003 56004 56005 56006 56007 56008 56009 56010 56011 56012 56013 56014 56015 56016 56017 56018 56019 56020 56021 56022 56023 56024 56025 56026 56027 56028 56029 56030 56031 56032 56033 56034 56035 56036 56037 56038 56039 56040 56041 56042 56043 56044 56045 56046 56047 56048 56049 56050 56051 56052 56053 56054 56055 56056 56057 56058 56059 56060 56061 56062 56063 56064 56065 56066 56067 56068 56069 56070 56071 56072 56073 56074 56075 56076 56077 56078 56079 56080 56081 56082 56083 56084 56085 56086 56087 56088 56089 56090 56091 56092 56093 56094 56095 56096 56097 56098 56099 56100 56101 56102 56103 56104 56105 56106 56107 56108 56109 56110 56111 56112 56113 56114 56115 56116 56117 56118 56119 56120 56121 56122 56123 56124 56125 56126 56127 56128 56129 56130 56131 56132 56133 56134 56135 56136 56137 56138 56139 56140 56141 56142 56143 56144 56145 56146 56147 56148 56149 56150 56151 56152 56153 56154 56155 56156 56157 56158 56159 56160 56161 56162 56163 56164 56165 56166 56167 56168 56169 56170 56171 56172 56173 56174 56175 56176 56177 56178 56179 56180 56181 56182 56183 56184 56185 56186 56187 56188 56189 56190 56191 56192 56193 56194 56195 56196 56197 56198 56199 56200 56201 56202 56203 56204 56205 56206 56207 56208 56209 56210 56211 56212 56213 56214 56215 56216 56217 56218 56219 56220 56221 56222 56223 56224 56225 56226 56227 56228 56229 56230 56231 56232 56233 56234 56235 56236 56237 56238 56239 56240 56241 56242 56243 56244 56245 56246 56247 56248 56249 56250 56251 56252 56253 56254 56255 56256 56257 56258 56259 56260 56261 56262 56263 56264 56265 56266 56267 56268 56269 56270 56271 56272 56273 56274 56275 56276 56277 56278 56279 56280 56281 56282 56283 56284 56285 56286 56287 56288 56289 56290 56291 56292 56293 56294 56295 56296 56297 56298 56299 56300 56301 56302 56303 56304 56305 56306 56307 56308 56309 56310 56311 56312 56313 56314 56315 56316 56317 56318 56319 56320 56321 56322 56323 56324 56325 56326 56327 56328 56329 56330 56331 56332 56333 56334 56335 56336 56337 56338 56339 56340 56341 56342 56343 56344 56345 56346 56347 56348 56349 56350 56351 56352 56353 56354 56355 56356 56357 56358 56359 56360 56361 56362 56363 56364 56365 56366 56367 56368 56369 56370 56371 56372 56373 56374 56375 56376 56377 56378 56379 56380 56381 56382 56383 56384 56385 56386 56387 56388 56389 56390 56391 56392 56393 56394 56395 56396 56397 56398 56399 56400 56401 56402 56403 56404 56405 56406 56407 56408 56409 56410 56411 56412 56413 56414 56415 56416 56417 56418 56419 56420 56421 56422 56423 56424 56425 56426 56427 56428 56429 56430 56431 56432 56433 56434 56435 56436 56437 56438 56439 56440 56441 56442 56443 56444 56445 56446 56447 56448 56449 56450 56451 56452 56453 56454 56455 56456 56457 56458 56459 56460 56461 56462 56463 56464 56465 56466 56467 56468 56469 56470 56471 56472 56473 56474 56475 56476 56477 56478 56479 56480 56481 56482 56483 56484 56485 56486 56487 56488 56489 56490 56491 56492 56493 56494 56495 56496 56497 56498 56499 56500 56501 56502 56503 56504 56505 56506 56507 56508 56509 56510 56511 56512 56513 56514 56515 56516 56517 56518 56519 56520 56521 56522 56523 56524 56525 56526 56527 56528 56529 56530 56531 56532 56533 56534 56535 56536 56537 56538 56539 56540 56541 56542 56543 56544 56545 56546 56547 56548 56549 56550 56551 56552 56553 56554 56555 56556 56557 56558 56559 56560 56561 56562 56563 56564 56565 56566 56567 56568 56569 56570 56571 56572 56573 56574 56575 56576 56577 56578 56579 56580 56581 56582 56583 56584 56585 56586 56587 56588 56589 56590 56591 56592 56593 56594 56595 56596 56597 56598 56599 56600 56601 56602 56603 56604 56605 56606 56607 56608 56609 56610 56611 56612 56613 56614 56615 56616 56617 56618 56619 56620 56621 56622 56623 56624 56625 56626 56627 56628 56629 56630 56631 56632 56633 56634 56635 56636 56637 56638 56639 56640 56641 56642 56643 56644 56645 56646 56647 56648 56649 56650 56651 56652 56653 56654 56655 56656 56657 56658 56659 56660 56661 56662 56663 56664 56665 56666 56667 56668 56669 56670 56671 56672 56673 56674 56675 56676 56677 56678 56679 56680 56681 56682 56683 56684 56685 56686 56687 56688 56689 56690 56691 56692 56693 56694 56695 56696 56697 56698 56699 56700 56701 56702 56703 56704 56705 56706 56707 56708 56709 56710 56711 56712 56713 56714 56715 56716 56717 56718 56719 56720 56721 56722 56723 56724 56725 56726 56727 56728 56729 56730 56731 56732 56733 56734 56735 56736 56737 56738 56739 56740 56741 56742 56743 56744 56745 56746 56747 56748 56749 56750 56751 56752 56753 56754 56755 56756 56757 56758 56759 56760 56761 56762 56763 56764 56765 56766 56767 56768 56769 56770 56771 56772 56773 56774 56775 56776 56777 56778 56779 56780 56781 56782 56783 56784 56785 56786 56787 56788 56789 56790 56791 56792 56793 56794 56795 56796 56797 56798 56799 56800 56801 56802 56803 56804 56805 56806 56807 56808 56809 56810 56811 56812 56813 56814 56815 56816 56817 56818 56819 56820 56821 56822 56823 56824 56825 56826 56827 56828 56829 56830 56831 56832 56833 56834 56835 56836 56837 56838 56839 56840 56841 56842 56843 56844 56845 56846 56847 56848 56849 56850 56851 56852 56853 56854 56855 56856 56857 56858 56859 56860 56861 56862 56863 56864 56865 56866 56867 56868 56869 56870 56871 56872 56873 56874 56875 56876 56877 56878 56879 56880 56881 56882 56883 56884 56885 56886 56887 56888 56889 56890 56891 56892 56893 56894 56895 56896 56897 56898 56899 56900 56901 56902 56903 56904 56905 56906 56907 56908 56909 56910 56911 56912 56913 56914 56915 56916 56917 56918 56919 56920 56921 56922 56923 56924 56925 56926 56927 56928 56929 56930 56931 56932 56933 56934 56935 56936 56937 56938 56939 56940 56941 56942 56943 56944 56945 56946 56947 56948 56949 56950 56951 56952 56953 56954 56955 56956 56957 56958 56959 56960 56961 56962 56963 56964 56965 56966 56967 56968 56969 56970 56971 56972 56973 56974 56975 56976 56977 56978 56979 56980 56981 56982 56983 56984 56985 56986 56987 56988 56989 56990 56991 56992 56993 56994 56995 56996 56997 56998 56999 57000
                                        "images(): Specified sub-list indices (%u->%u) are out of bounds.",
                                        cimglist_instance,
                                        pos0,pos1);
          CImgList<T> res(pos1 - pos0 + 1);
          cimglist_for(res,l) res[l].assign(_data[pos0 + l]);
          return res;
        }
    
        //! Return a shared sublist.
        /**
          \param pos0 Starting index of the sublist.
          \param pos1 Ending index of the sublist.
        **/
        CImgList<T> get_shared_images(const unsigned int pos0, const unsigned int pos1) {
          if (pos0>pos1 || pos1>=_width)
            throw CImgArgumentException(_cimglist_instance
                                        "get_shared_images(): Specified sub-list indices (%u->%u) are out of bounds.",
                                        cimglist_instance,
                                        pos0,pos1);
          CImgList<T> res(pos1 - pos0 + 1);
          cimglist_for(res,l) res[l].assign(_data[pos0 + l],_data[pos0 + l]?true:false);
          return res;
        }
    
        //! Return a shared sublist \newinstance.
        const CImgList<T> get_shared_images(const unsigned int pos0, const unsigned int pos1) const {
          if (pos0>pos1 || pos1>=_width)
            throw CImgArgumentException(_cimglist_instance
                                        "get_shared_images(): Specified sub-list indices (%u->%u) are out of bounds.",
                                        cimglist_instance,
                                        pos0,pos1);
          CImgList<T> res(pos1 - pos0 + 1);
          cimglist_for(res,l) res[l].assign(_data[pos0 + l],_data[pos0 + l]?true:false);
          return res;
        }
    
        //! Return a single image which is the appending of all images of the current CImgList instance.
        /**
           \param axis Appending axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
           \param align Appending alignment.
        **/
        CImg<T> get_append(const char axis, const float align=0) const {
          if (is_empty()) return CImg<T>();
          if (_width==1) return +((*this)[0]);
          unsigned int dx = 0, dy = 0, dz = 0, dc = 0, pos = 0;
          CImg<T> res;
          switch (cimg::lowercase(axis)) {
          case 'x' : { // Along the X-axis.
            cimglist_for(*this,l) {
              const CImg<T>& img = (*this)[l];
              if (img) {
                dx+=img._width;
                dy = std::max(dy,img._height);
                dz = std::max(dz,img._depth);
                dc = std::max(dc,img._spectrum);
              }
            }
            res.assign(dx,dy,dz,dc,(T)0);
            if (res) cimglist_for(*this,l) {
                const CImg<T>& img = (*this)[l];
                if (img) res.draw_image(pos,
                                        (int)(align*(dy - img._height)),
                                        (int)(align*(dz - img._depth)),
                                        (int)(align*(dc - img._spectrum)),
                                        img);
                pos+=img._width;
              }
          } break;
          case 'y' : { // Along the Y-axis.
            cimglist_for(*this,l) {
              const CImg<T>& img = (*this)[l];
              if (img) {
                dx = std::max(dx,img._width);
                dy+=img._height;
                dz = std::max(dz,img._depth);
                dc = std::max(dc,img._spectrum);
              }
            }
            res.assign(dx,dy,dz,dc,(T)0);
            if (res) cimglist_for(*this,l) {
                const CImg<T>& img = (*this)[l];
                if (img) res.draw_image((int)(align*(dx - img._width)),
                                        pos,
                                        (int)(align*(dz - img._depth)),
                                        (int)(align*(dc - img._spectrum)),
                                        img);
                pos+=img._height;
              }
          } break;
          case 'z' : { // Along the Z-axis.
            cimglist_for(*this,l) {
              const CImg<T>& img = (*this)[l];
              if (img) {
                dx = std::max(dx,img._width);
                dy = std::max(dy,img._height);
                dz+=img._depth;
                dc = std::max(dc,img._spectrum);
              }
            }
            res.assign(dx,dy,dz,dc,(T)0);
            if (res) cimglist_for(*this,l) {
                const CImg<T>& img = (*this)[l];
                if (img) res.draw_image((int)(align*(dx - img._width)),
                                        (int)(align*(dy - img._height)),
                                        pos,
                                        (int)(align*(dc - img._spectrum)),
                                        img);
                pos+=img._depth;
              }
          } break;
          default : { // Along the C-axis.
            cimglist_for(*this,l) {
              const CImg<T>& img = (*this)[l];
              if (img) {
                dx = std::max(dx,img._width);
                dy = std::max(dy,img._height);
                dz = std::max(dz,img._depth);
                dc+=img._spectrum;
              }
            }
            res.assign(dx,dy,dz,dc,(T)0);
            if (res) cimglist_for(*this,l) {
                const CImg<T>& img = (*this)[l];
                if (img) res.draw_image((int)(align*(dx - img._width)),
                                        (int)(align*(dy - img._height)),
                                        (int)(align*(dz - img._depth)),
                                        pos,
                                        img);
                pos+=img._spectrum;
              }
          }
          }
          return res;
        }
    
        //! Return a list where each image has been split along the specified axis.
        /**
            \param axis Axis to split images along.
            \param nb Number of spliting parts for each image.
        **/
        CImgList<T>& split(const char axis, const int nb=-1) {
          return get_split(axis,nb).move_to(*this);
        }
    
        //! Return a list where each image has been split along the specified axis \newinstance.
        CImgList<T> get_split(const char axis, const int nb=-1) const {
          CImgList<T> res;
          cimglist_for(*this,l) _data[l].get_split(axis,nb).move_to(res,~0U);
          return res;
        }
    
        //! Insert image at the end of the list.
        /**
          \param img Image to insert.
        **/
        template<typename t>
        CImgList<T>& push_back(const CImg<t>& img) {
          return insert(img);
        }
    
        //! Insert image at the front of the list.
        /**
          \param img Image to insert.
        **/
        template<typename t>
        CImgList<T>& push_front(const CImg<t>& img) {
          return insert(img,0);
        }
    
        //! Insert list at the end of the current list.
        /**
          \param list List to insert.
        **/
        template<typename t>
        CImgList<T>& push_back(const CImgList<t>& list) {
          return insert(list);
        }
    
        //! Insert list at the front of the current list.
        /**
          \param list List to insert.
        **/
        template<typename t>
        CImgList<T>& push_front(const CImgList<t>& list) {
          return insert(list,0);
        }
    
        //! Remove last image.
        /**
        **/
        CImgList<T>& pop_back() {
          return remove(_width - 1);
        }
    
        //! Remove first image.
        /**
        **/
        CImgList<T>& pop_front() {
          return remove(0);
        }
    
        //! Remove image pointed by iterator.
        /**
          \param iter Iterator pointing to the image to remove.
        **/
        CImgList<T>& erase(const iterator iter) {
          return remove(iter - _data);
        }
    
        //@}
        //----------------------------------
        //
        //! \name Data Input
        //@{
        //----------------------------------
    
        //! Display a simple interactive interface to select images or sublists.
        /**
           \param disp Window instance to display selection and user interface.
           \param feature_type Can be \c false to select a single image, or \c true to select a sublist.
           \param axis Axis along whom images are appended for visualization.
           \param align Alignment setting when images have not all the same size.
           \return A one-column vector containing the selected image indexes.
        **/
        CImg<intT> get_select(CImgDisplay &disp, const bool feature_type=true,
                              const char axis='x', const float align=0,
                              const bool exit_on_anykey=false) const {
          return _select(disp,0,feature_type,axis,align,exit_on_anykey,0,false,false,false);
        }
    
        //! Display a simple interactive interface to select images or sublists.
        /**
           \param title Title of a new window used to display selection and user interface.
           \param feature_type Can be \c false to select a single image, or \c true to select a sublist.
           \param axis Axis along whom images are appended for visualization.
           \param align Alignment setting when images have not all the same size.
           \return A one-column vector containing the selected image indexes.
        **/
        CImg<intT> get_select(const char *const title, const bool feature_type=true,
                              const char axis='x', const float align=0,
                              const bool exit_on_anykey=false) const {
          CImgDisplay disp;
          return _select(disp,title,feature_type,axis,align,exit_on_anykey,0,false,false,false);
        }
    
        CImg<intT> _select(CImgDisplay &disp, const char *const title, const bool feature_type,
                           const char axis, const float align, const bool exit_on_anykey,
                           const unsigned int orig, const bool resize_disp,
                           const bool exit_on_rightbutton, const bool exit_on_wheel) const {
          if (is_empty())
            throw CImgInstanceException(_cimglist_instance
                                        "select(): Empty instance.",
                                        cimglist_instance);
    
          // Create image correspondence table and get list dimensions for visualization.
          CImgList<uintT> _indices;
          unsigned int max_width = 0, max_height = 0, sum_width = 0, sum_height = 0;
          cimglist_for(*this,l) {
            const CImg<T>& img = _data[l];
            const unsigned int
              w = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,false),
              h = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,true);
            if (w>max_width) max_width = w;
            if (h>max_height) max_height = h;
            sum_width+=w; sum_height+=h;
            if (axis=='x') CImg<uintT>(w,1,1,1,(unsigned int)l).move_to(_indices);
            else CImg<uintT>(h,1,1,1,(unsigned int)l).move_to(_indices);
          }
          const CImg<uintT> indices0 = _indices>'x';
    
          // Create display window.
          if (!disp) {
            if (axis=='x') disp.assign(cimg_fitscreen(sum_width,max_height,1),title?title:0,1);
            else disp.assign(cimg_fitscreen(max_width,sum_height,1),title?title:0,1);
            if (!title) disp.set_title("CImgList<%s> (%u)",pixel_type(),_width);
          } else if (title) disp.set_title("%s",title);
          if (resize_disp) {
            if (axis=='x') disp.resize(cimg_fitscreen(sum_width,max_height,1),false);
            else disp.resize(cimg_fitscreen(max_width,sum_height,1),false);
          }
    
          const unsigned int old_normalization = disp.normalization();
          bool old_is_resized = disp.is_resized();
          disp._normalization = 0;
          disp.show().set_key(0);
          static const unsigned char foreground_color[] = { 255,255,255 }, background_color[] = { 0,0,0 };
    
          // Enter event loop.
          CImg<ucharT> visu0, visu;
          CImg<uintT> indices;
          CImg<intT> positions(_width,4,1,1,-1);
          int oindice0 = -1, oindice1 = -1, indice0 = -1, indice1 = -1;
          bool is_clicked = false, is_selected = false, text_down = false, update_display = true;
          unsigned int key = 0;
    
          while (!is_selected && !disp.is_closed() && !key) {
    
            // Create background image.
            if (!visu0) {
              visu0.assign(disp._width,disp._height,1,3,0); visu.assign();
              (indices0.get_resize(axis=='x'?visu0._width:visu0._height,1)).move_to(indices);
              unsigned int ind = 0;
              if (axis=='x') for (unsigned int x = 0; x<visu0._width; ) {
                  const unsigned int x0 = x;
                  ind = indices[x];
                  while (x<indices._width && indices[x++]==ind) {}
                  const CImg<T>
                    onexone(1,1,1,1,(T)0),
                    &src = _data[ind]?_data[ind]:onexone;
                  CImg<ucharT> res;
                  src.__get_select(disp,old_normalization,(src._width - 1)/2,(src._height - 1)/2,(src._depth - 1)/2).
                    move_to(res);
                  const unsigned int h = CImgDisplay::_fitscreen(res._width,res._height,1,128,-85,true);
                  res.resize(x - x0,std::max(32U,h*disp._height/max_height),1,res._spectrum==1?3:-100);
                  positions(ind,0) = positions(ind,2) = (int)x0;
                  positions(ind,1) = positions(ind,3) = (int)(align*(visu0.height() - res.height()));
                  positions(ind,2)+=res._width;
                  positions(ind,3)+=res._height - 1;
                  visu0.draw_image(positions(ind,0),positions(ind,1),res);
                } else for (unsigned int y = 0; y<visu0._height; ) {
                  const unsigned int y0 = y;
                  ind = indices[y];
                  while (y<visu0._height && indices[++y]==ind) {}
                  const CImg<T> &src = _data[ind];
                  const CImg<Tuchar>
                    img2d = src._depth>1?src.get_projections2d((src._width - 1)/2,(src._height - 1)/2,(src._depth - 1)/2):
                    cimg::type<Tuchar>::string()==cimg::type<T>::string()?src.get_shared():src;
                  CImg<ucharT> res = old_normalization==1 ||
                    (old_normalization==3 && cimg::type<T>::string()!=cimg::type<unsigned char>::string())?
                    CImg<ucharT>(img2d.get_normalize((Tuchar)0,(Tuchar)255)):
                    CImg<ucharT>(img2d);
                  if (res._spectrum>3) res.channels(0,2);
                  const unsigned int w = CImgDisplay::_fitscreen(res._width,res._height,1,128,-85,false);
                  res.resize(std::max(32U,w*disp._width/max_width),y - y0,1,res._spectrum==1?3:-100);
                  positions(ind,0) = positions(ind,2) = (int)(align*(visu0.width() - res.width()));
                  positions(ind,1) = positions(ind,3) = (int)y0;
                  positions(ind,2)+=res._width - 1;
                  positions(ind,3)+=res._height;
                  visu0.draw_image(positions(ind,0),positions(ind,1),res);
                }
              if (axis=='x') --positions(ind,2); else --positions(ind,3);
              update_display = true;
            }
    
            if (!visu || oindice0!=indice0 || oindice1!=indice1) {
              if (indice0>=0 && indice1>=0) {
                visu.assign(visu0,false);
                const int indm = std::min(indice0,indice1), indM = std::max(indice0,indice1);
                for (int ind = indm; ind<=indM; ++ind) if (positions(ind,0)>=0) {
                    visu.draw_rectangle(positions(ind,0),positions(ind,1),positions(ind,2),positions(ind,3),
                                        background_color,0.2f);
                    if ((axis=='x' && positions(ind,2) - positions(ind,0)>=8) ||
                        (axis!='x' && positions(ind,3) - positions(ind,1)>=8))
                      visu.draw_rectangle(positions(ind,0),positions(ind,1),positions(ind,2),positions(ind,3),
                                          foreground_color,0.9f,0xAAAAAAAA);
                  }
                const int yt = (int)text_down?visu.height() - 13:0;
                if (is_clicked) visu.draw_text(0,yt," Images #%u - #%u, Size = %u",
                                               foreground_color,background_color,0.7f,13,
                                               orig + indm,orig + indM,indM - indm + 1);
                else visu.draw_text(0,yt," Image #%u (%u,%u,%u,%u)",foreground_color,background_color,0.7f,13,
                                    orig + indice0,
                                    _data[indice0]._width,
                                    _data[indice0]._height,
                                    _data[indice0]._depth,
                                    _data[indice0]._spectrum);
                update_display = true;
              } else visu.assign();
            }
            if (!visu) { visu.assign(visu0,true); update_display = true; }
            if (update_display) { visu.display(disp); update_display = false; }
            disp.wait();
    
            // Manage user events.
            const int xm = disp.mouse_x(), ym = disp.mouse_y();
            int indice = -1;
    
            if (xm>=0) {
              indice = (int)indices(axis=='x'?xm:ym);
              if (disp.button()&1) {
                if (!is_clicked) { is_clicked = true; oindice0 = indice0; indice0 = indice; }
                oindice1 = indice1; indice1 = indice;
                if (!feature_type) is_selected = true;
              } else {
                if (!is_clicked) { oindice0 = oindice1 = indice0; indice0 = indice1 = indice; }
                else is_selected = true;
              }
            } else {
              if (is_clicked) {
                if (!(disp.button()&1)) { is_clicked = is_selected = false; indice0 = indice1 = -1; }
                else indice1 = -1;
              } else indice0 = indice1 = -1;
            }
    
            if (disp.button()&4) { is_clicked = is_selected = false; indice0 = indice1 = -1; }
            if (disp.button()&2 && exit_on_rightbutton) { is_selected = true; indice1 = indice0 = -1; }
            if (disp.wheel() && exit_on_wheel) is_selected = true;
    
            CImg<charT> filename(32);
            switch (key = disp.key()) {
    #if cimg_OS!=2
            case cimg::keyCTRLRIGHT :
    #endif
            case 0 : case cimg::keyCTRLLEFT : key = 0; break;
            case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
                disp.set_fullscreen(false).
                  resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
                         CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false).
                  _is_resized = true;
                disp.set_key(key,false); key = 0; visu0.assign();
              } break;
            case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
                disp.set_fullscreen(false).
                  resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true;
                disp.set_key(key,false); key = 0; visu0.assign();
              } break;
            case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
                disp.set_fullscreen(false).
                  resize(cimg_fitscreen(axis=='x'?sum_width:max_width,axis=='x'?max_height:sum_height,1),false).
                  _is_resized = true;
                disp.set_key(key,false); key = 0; visu0.assign();
              } break;
            case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
                disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen()._is_resized = true;
                disp.set_key(key,false); key = 0; visu0.assign();
              } break;
            case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
                static unsigned int snap_number = 0;
                std::FILE *file;
                do {
                  cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.bmp",snap_number++);
                  if ((file=std_fopen(filename,"r"))!=0) cimg::fclose(file);
                } while (file);
                if (visu0) {
                  (+visu0).draw_text(0,0," Saving snapshot... ",
                                     foreground_color,background_color,0.7f,13).display(disp);
                  visu0.save(filename);
                  (+visu0).draw_text(0,0," Snapshot '%s' saved. ",
                                     foreground_color,background_color,0.7f,13,filename._data).display(disp);
                }
                disp.set_key(key,false).wait(); key = 0;
              } break;
            case cimg::keyO :
              if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
                static unsigned int snap_number = 0;
                std::FILE *file;
                do {
    #ifdef cimg_use_zlib
                  cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++);
    #else
                  cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimg",snap_number++);
    #endif
                  if ((file=std_fopen(filename,"r"))!=0) cimg::fclose(file);
                } while (file);
                (+visu0).draw_text(0,0," Saving instance... ",
                                   foreground_color,background_color,0.7f,13).display(disp);
                save(filename);
                (+visu0).draw_text(0,0," Instance '%s' saved. ",
                                   foreground_color,background_color,0.7f,13,filename._data).display(disp);
                disp.set_key(key,false).wait(); key = 0;
              } break;
            }
            if (disp.is_resized()) { disp.resize(false); visu0.assign(); }
            if (ym>=0 && ym<13) { if (!text_down) { visu.assign(); text_down = true; }}
            else if (ym>=visu.height() - 13) { if (text_down) { visu.assign(); text_down = false; }}
            if (!exit_on_anykey && key && key!=cimg::keyESC &&
                (key!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) {
              key = 0;
            }
          }
          CImg<intT> res(1,2,1,1,-1);
          if (is_selected) {
            if (feature_type) res.fill(std::min(indice0,indice1),std::max(indice0,indice1));
            else res.fill(indice0);
          }
          if (!(disp.button()&2)) disp.set_button();
          disp._normalization = old_normalization;
          disp._is_resized = old_is_resized;
          disp.set_key(key);
          return res;
        }
    
        //! Load a list from a file.
        /**
         \param filename Filename to read data from.
        **/
        CImgList<T>& load(const char *const filename) {
          if (!filename)
            throw CImgArgumentException(_cimglist_instance
                                        "load(): Specified filename is (null).",
                                        cimglist_instance);
    
          if (!cimg::strncasecmp(filename,"http://",7) || !cimg::strncasecmp(filename,"https://",8)) {
            CImg<charT> filename_local(256);
            load(cimg::load_network(filename,filename_local));
            std::remove(filename_local);
            return *this;
          }
    
          const bool is_stdin = *filename=='-' && (!filename[1] || filename[1]=='.');
          const char *const ext = cimg::split_filename(filename);
          const unsigned int omode = cimg::exception_mode();
          cimg::exception_mode(0);
          bool is_loaded = true;
          try {
    #ifdef cimglist_load_plugin
            cimglist_load_plugin(filename);
    #endif
    #ifdef cimglist_load_plugin1
            cimglist_load_plugin1(filename);
    #endif
    #ifdef cimglist_load_plugin2
            cimglist_load_plugin2(filename);
    #endif
    #ifdef cimglist_load_plugin3
            cimglist_load_plugin3(filename);
    #endif
    #ifdef cimglist_load_plugin4
            cimglist_load_plugin4(filename);
    #endif
    #ifdef cimglist_load_plugin5
            cimglist_load_plugin5(filename);
    #endif
    #ifdef cimglist_load_plugin6
            cimglist_load_plugin6(filename);
    #endif
    #ifdef cimglist_load_plugin7
            cimglist_load_plugin7(filename);
    #endif
    #ifdef cimglist_load_plugin8
            cimglist_load_plugin8(filename);
    #endif
            if (!cimg::strcasecmp(ext,"tif") ||
                !cimg::strcasecmp(ext,"tiff")) load_tiff(filename);
            else if (!cimg::strcasecmp(ext,"gif")) load_gif_external(filename);
            else if (!cimg::strcasecmp(ext,"cimg") ||
                     !cimg::strcasecmp(ext,"cimgz") ||
                     !*ext) load_cimg(filename);
            else if (!cimg::strcasecmp(ext,"rec") ||
                     !cimg::strcasecmp(ext,"par")) load_parrec(filename);
            else if (!cimg::strcasecmp(ext,"avi") ||
                     !cimg::strcasecmp(ext,"mov") ||
                     !cimg::strcasecmp(ext,"asf") ||
                     !cimg::strcasecmp(ext,"divx") ||
                     !cimg::strcasecmp(ext,"flv") ||
                     !cimg::strcasecmp(ext,"mpg") ||
                     !cimg::strcasecmp(ext,"m1v") ||
                     !cimg::strcasecmp(ext,"m2v") ||
                     !cimg::strcasecmp(ext,"m4v") ||
                     !cimg::strcasecmp(ext,"mjp") ||
                     !cimg::strcasecmp(ext,"mp4") ||
                     !cimg::strcasecmp(ext,"mkv") ||
                     !cimg::strcasecmp(ext,"mpe") ||
                     !cimg::strcasecmp(ext,"movie") ||
                     !cimg::strcasecmp(ext,"ogm") ||
                     !cimg::strcasecmp(ext,"ogg") ||
                     !cimg::strcasecmp(ext,"ogv") ||
                     !cimg::strcasecmp(ext,"qt") ||
                     !cimg::strcasecmp(ext,"rm") ||
                     !cimg::strcasecmp(ext,"vob") ||
                     !cimg::strcasecmp(ext,"wmv") ||
                     !cimg::strcasecmp(ext,"xvid") ||
                     !cimg::strcasecmp(ext,"mpeg")) load_video(filename);
            else if (!cimg::strcasecmp(ext,"gz")) load_gzip_external(filename);
            else is_loaded = false;
          } catch (CImgIOException&) { is_loaded = false; }
    
          // If nothing loaded, try to guess file format from magic number in file.
          if (!is_loaded && !is_stdin) {
            std::FILE *const file = std_fopen(filename,"rb");
            if (!file) {
              cimg::exception_mode(omode);
              throw CImgIOException(_cimglist_instance
                                    "load(): Failed to open file '%s'.",
                                    cimglist_instance,
                                    filename);
            }
    
            const char *const f_type = cimg::ftype(file,filename);
            std::fclose(file);
            is_loaded = true;
            try {
              if (!cimg::strcasecmp(f_type,"gif")) load_gif_external(filename);
              else if (!cimg::strcasecmp(f_type,"tif")) load_tiff(filename);
              else is_loaded = false;
            } catch (CImgIOException&) { is_loaded = false; }
          }
    
          // If nothing loaded, try to load file as a single image.
          if (!is_loaded) {
            assign(1);
            try {
              _data->load(filename);
            } catch (CImgIOException&) {
              cimg::exception_mode(omode);
              throw CImgIOException(_cimglist_instance
                                    "load(): Failed to recognize format of file '%s'.",
                                    cimglist_instance,
                                    filename);
            }
          }
          cimg::exception_mode(omode);
          return *this;
        }
    
        //! Load a list from a file \newinstance.
        static CImgList<T> get_load(const char *const filename) {
          return CImgList<T>().load(filename);
        }
    
        //! Load a list from a .cimg file.
        /**
          \param filename Filename to read data from.
        **/
        CImgList<T>& load_cimg(const char *const filename) {
          return _load_cimg(0,filename);
        }
    
        //! Load a list from a .cimg file \newinstance.
        static CImgList<T> get_load_cimg(const char *const filename) {
          return CImgList<T>().load_cimg(filename);
        }
    
        //! Load a list from a .cimg file.
        /**
          \param file File to read data from.
        **/
        CImgList<T>& load_cimg(std::FILE *const file) {
          return _load_cimg(file,0);
        }
    
        //! Load a list from a .cimg file \newinstance.
        static CImgList<T> get_load_cimg(std::FILE *const file) {
          return CImgList<T>().load_cimg(file);
        }
    
        CImgList<T>& _load_cimg(std::FILE *const file, const char *const filename) {
    #ifdef cimg_use_zlib
    #define _cimgz_load_cimg_case(Tss) { \
       Bytef *const cbuf = new Bytef[csiz]; \
       cimg::fread(cbuf,csiz,nfile); \
       raw.assign(W,H,D,C); \
       uLongf destlen = (ulongT)raw.size()*sizeof(Tss); \
       uncompress((Bytef*)raw._data,&destlen,cbuf,csiz); \
       delete[] cbuf; \
       if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw.size()); \
       raw.move_to(img); \
    }
    #else
    #define _cimgz_load_cimg_case(Tss) \
       throw CImgIOException(_cimglist_instance \
                             "load_cimg(): Unable to load compressed data from file '%s' unless zlib is enabled.", \
                             cimglist_instance, \
                             filename?filename:"(FILE*)");
    #endif
    
    #define _cimg_load_cimg_case(Ts,Tss) \
          if (!loaded && !cimg::strcasecmp(Ts,str_pixeltype)) { \
            for (unsigned int l = 0; l<N; ++l) { \
              j = 0; while ((i=std::fgetc(nfile))!='\n' && i>=0 && j<255) tmp[j++] = (char)i; tmp[j] = 0; \
              W = H = D = C = 0; csiz = 0; \
              if ((err = cimg_sscanf(tmp,"%u %u %u %u #%lu",&W,&H,&D,&C,&csiz))<4) \
                throw CImgIOException(_cimglist_instance \
                                      "load_cimg(): Invalid specified size (%u,%u,%u,%u) of image %u in file '%s'.", \
                                      cimglist_instance, \
                                      W,H,D,C,l,filename?filename:("(FILE*)")); \
              if (W*H*D*C>0) { \
                CImg<Tss> raw; \
                CImg<T> &img = _data[l]; \
                if (err==5) _cimgz_load_cimg_case(Tss) \
                else { \
                  img.assign(W,H,D,C); \
                  T *ptrd = img._data; \
                  for (ulongT to_read = img.size(); to_read; ) { \
                    raw.assign((unsigned int)std::min(to_read,cimg_iobuffer)); \
                    cimg::fread(raw._data,raw._width,nfile); \
                    if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw.size()); \
                    const Tss *ptrs = raw._data; \
                    for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); \
                    to_read-=raw._width; \
                  } \
                } \
              } \
            } \
            loaded = true; \
          }
    
          if (!filename && !file)
            throw CImgArgumentException(_cimglist_instance
                                        "load_cimg(): Specified filename is (null).",
                                        cimglist_instance);
    
          const ulongT cimg_iobuffer = (ulongT)24*1024*1024;
          std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
          bool loaded = false, endian = cimg::endianness();
          CImg<charT> tmp(256), str_pixeltype(256), str_endian(256);
          *tmp = *str_pixeltype = *str_endian = 0;
          unsigned int j, N = 0, W, H, D, C;
          unsigned long csiz;
          int i, err;
          do {
            j = 0; while ((i=std::fgetc(nfile))!='\n' && i>=0 && j<255) tmp[j++] = (char)i; tmp[j] = 0;
          } while (*tmp=='#' && i>=0);
          err = cimg_sscanf(tmp,"%u%*c%255[A-Za-z64_]%*c%255[sA-Za-z_ ]",
                            &N,str_pixeltype._data,str_endian._data);
          if (err<2) {
            if (!file) cimg::fclose(nfile);
            throw CImgIOException(_cimglist_instance
                                  "load_cimg(): CImg header not found in file '%s'.",
                                  cimglist_instance,
                                  filename?filename:"(FILE*)");
          }
          if (!cimg::strncasecmp("little",str_endian,6)) endian = false;
          else if (!cimg::strncasecmp("big",str_endian,3)) endian = true;
          assign(N);
          _cimg_load_cimg_case("bool",bool);
          _cimg_load_cimg_case("unsigned_char",unsigned char);
          _cimg_load_cimg_case("uchar",unsigned char);
          _cimg_load_cimg_case("char",char);
          _cimg_load_cimg_case("unsigned_short",unsigned short);
          _cimg_load_cimg_case("ushort",unsigned short);
          _cimg_load_cimg_case("short",short);
          _cimg_load_cimg_case("unsigned_int",unsigned int);
          _cimg_load_cimg_case("uint",unsigned int);
          _cimg_load_cimg_case("int",int);
          _cimg_load_cimg_case("unsigned_long",ulongT);
          _cimg_load_cimg_case("ulong",ulongT);
          _cimg_load_cimg_case("long",longT);
          _cimg_load_cimg_case("unsigned_int64",uint64T);
          _cimg_load_cimg_case("uint64",uint64T);
          _cimg_load_cimg_case("int64",int64T);
          _cimg_load_cimg_case("float",float);
          _cimg_load_cimg_case("double",double);
    
          if (!loaded) {
            if (!file) cimg::fclose(nfile);
            throw CImgIOException(_cimglist_instance
                                  "load_cimg(): Unsupported pixel type '%s' for file '%s'.",
                                  cimglist_instance,
                                  str_pixeltype._data,filename?filename:"(FILE*)");
          }
          if (!file) cimg::fclose(nfile);
          return *this;
        }
    
        //! Load a sublist list from a (non compressed) .cimg file.
        /**
          \param filename Filename to read data from.
          \param n0 Starting index of images to read (~0U for max).
          \param n1 Ending index of images to read (~0U for max).
          \param x0 Starting X-coordinates of image regions to read.
          \param y0 Starting Y-coordinates of image regions to read.
          \param z0 Starting Z-coordinates of image regions to read.
          \param c0 Starting C-coordinates of image regions to read.
          \param x1 Ending X-coordinates of image regions to read (~0U for max).
          \param y1 Ending Y-coordinates of image regions to read (~0U for max).
          \param z1 Ending Z-coordinates of image regions to read (~0U for max).
          \param c1 Ending C-coordinates of image regions to read (~0U for max).
        **/
        CImgList<T>& load_cimg(const char *const filename,
                               const unsigned int n0, const unsigned int n1,
                               const unsigned int x0, const unsigned int y0,
                               const unsigned int z0, const unsigned int c0,
                               const unsigned int x1, const unsigned int y1,
                               const unsigned int z1, const unsigned int c1) {
          return _load_cimg(0,filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
        }
    
        //! Load a sublist list from a (non compressed) .cimg file \newinstance.
        static CImgList<T> get_load_cimg(const char *const filename,
                                         const unsigned int n0, const unsigned int n1,
                                         const unsigned int x0, const unsigned int y0,
                                         const unsigned int z0, const unsigned int c0,
                                         const unsigned int x1, const unsigned int y1,
                                         const unsigned int z1, const unsigned int c1) {
          return CImgList<T>().load_cimg(filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
        }
    
        //! Load a sub-image list from a (non compressed) .cimg file \overloading.
        CImgList<T>& load_cimg(std::FILE *const file,
                               const unsigned int n0, const unsigned int n1,
                               const unsigned int x0, const unsigned int y0,
                               const unsigned int z0, const unsigned int c0,
                               const unsigned int x1, const unsigned int y1,
                               const unsigned int z1, const unsigned int c1) {
          return _load_cimg(file,0,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
        }
    
        //! Load a sub-image list from a (non compressed) .cimg file \newinstance.
        static CImgList<T> get_load_cimg(std::FILE *const file,
                                         const unsigned int n0, const unsigned int n1,
                                         const unsigned int x0, const unsigned int y0,
                                         const unsigned int z0, const unsigned int c0,
                                         const unsigned int x1, const unsigned int y1,
                                         const unsigned int z1, const unsigned int c1) {
          return CImgList<T>().load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
        }
    
        CImgList<T>& _load_cimg(std::FILE *const file, const char *const filename,
                                const unsigned int n0, const unsigned int n1,
                                const unsigned int x0, const unsigned int y0,
                                const unsigned int z0, const unsigned int c0,
                                const unsigned int x1, const unsigned int y1,
                                const unsigned int z1, const unsigned int c1) {
    #define _cimg_load_cimg_case2(Ts,Tss) \
          if (!loaded && !cimg::strcasecmp(Ts,str_pixeltype)) { \
            for (unsigned int l = 0; l<=nn1; ++l) { \
              j = 0; while ((i=std::fgetc(nfile))!='\n' && i>=0) tmp[j++] = (char)i; tmp[j] = 0; \
              W = H = D = C = 0; \
              if (cimg_sscanf(tmp,"%u %u %u %u",&W,&H,&D,&C)!=4) \
                throw CImgIOException(_cimglist_instance \
                                      "load_cimg(): Invalid specified size (%u,%u,%u,%u) of image %u in file '%s'", \
                                      cimglist_instance, \
                                      W,H,D,C,l,filename?filename:"(FILE*)"); \
              if (W*H*D*C>0) { \
                if (l<nn0 || nx0>=W || ny0>=H || nz0>=D || nc0>=C) cimg::fseek(nfile,W*H*D*C*sizeof(Tss),SEEK_CUR); \
                else { \
                  const unsigned int \
                    _nx1 = nx1==~0U?W - 1:nx1, \
                    _ny1 = ny1==~0U?H - 1:ny1, \
                    _nz1 = nz1==~0U?D - 1:nz1, \
                    _nc1 = nc1==~0U?C - 1:nc1; \
                  if (_nx1>=W || _ny1>=H || _nz1>=D || _nc1>=C) \
                    throw CImgArgumentException(_cimglist_instance \
                                                "load_cimg(): Invalid specified coordinates " \
                                                "[%u](%u,%u,%u,%u) -> [%u](%u,%u,%u,%u) " \
                                                "because image [%u] in file '%s' has size (%u,%u,%u,%u).", \
                                                cimglist_instance, \
                                                n0,x0,y0,z0,c0,n1,x1,y1,z1,c1,l,filename?filename:"(FILE*)",W,H,D,C); \
                  CImg<Tss> raw(1 + _nx1 - nx0); \
                  CImg<T> &img = _data[l - nn0]; \
                  img.assign(1 + _nx1 - nx0,1 + _ny1 - ny0,1 + _nz1 - nz0,1 + _nc1 - nc0); \
                  T *ptrd = img._data; \
                  ulongT skipvb = nc0*W*H*D*sizeof(Tss); \
                  if (skipvb) cimg::fseek(nfile,skipvb,SEEK_CUR); \
                  for (unsigned int c = 1 + _nc1 - nc0; c; --c) { \
                    const ulongT skipzb = nz0*W*H*sizeof(Tss); \
                    if (skipzb) cimg::fseek(nfile,skipzb,SEEK_CUR); \
                    for (unsigned int z = 1 + _nz1 - nz0; z; --z) { \
                      const ulongT skipyb = ny0*W*sizeof(Tss); \
                      if (skipyb) cimg::fseek(nfile,skipyb,SEEK_CUR); \
                      for (unsigned int y = 1 + _ny1 - ny0; y; --y) { \
                        const ulongT skipxb = nx0*sizeof(Tss); \
                        if (skipxb) cimg::fseek(nfile,skipxb,SEEK_CUR); \
                        cimg::fread(raw._data,raw._width,nfile); \
                        if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw._width); \
                        const Tss *ptrs = raw._data; \
                        for (unsigned int off = raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); \
                        const ulongT skipxe = (W - 1 - _nx1)*sizeof(Tss); \
                        if (skipxe) cimg::fseek(nfile,skipxe,SEEK_CUR); \
                      } \
                      const ulongT skipye = (H - 1 - _ny1)*W*sizeof(Tss); \
                      if (skipye) cimg::fseek(nfile,skipye,SEEK_CUR); \
                    } \
                    const ulongT skipze = (D - 1 - _nz1)*W*H*sizeof(Tss); \
                    if (skipze) cimg::fseek(nfile,skipze,SEEK_CUR); \
                  } \
                  const ulongT skipve = (C - 1 - _nc1)*W*H*D*sizeof(Tss); \
                  if (skipve) cimg::fseek(nfile,skipve,SEEK_CUR); \
                } \
              } \
            } \
            loaded = true; \
          }
    
          if (!filename && !file)
            throw CImgArgumentException(_cimglist_instance
                                        "load_cimg(): Specified filename is (null).",
                                        cimglist_instance);
          unsigned int
            nn0 = std::min(n0,n1), nn1 = std::max(n0,n1),
            nx0 = std::min(x0,x1), nx1 = std::max(x0,x1),
            ny0 = std::min(y0,y1), ny1 = std::max(y0,y1),
            nz0 = std::min(z0,z1), nz1 = std::max(z0,z1),
            nc0 = std::min(c0,c1), nc1 = std::max(c0,c1);
    
          std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
          bool loaded = false, endian = cimg::endianness();
          CImg<charT> tmp(256), str_pixeltype(256), str_endian(256);
          *tmp = *str_pixeltype = *str_endian = 0;
          unsigned int j, N, W, H, D, C;
          int i, err;
          j = 0; while ((i=std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = 0;
          err = cimg_sscanf(tmp,"%u%*c%255[A-Za-z64_]%*c%255[sA-Za-z_ ]",
                            &N,str_pixeltype._data,str_endian._data);
          if (err<2) {
            if (!file) cimg::fclose(nfile);
            throw CImgIOException(_cimglist_instance
                                  "load_cimg(): CImg header not found in file '%s'.",
                                  cimglist_instance,
                                  filename?filename:"(FILE*)");
          }
          if (!cimg::strncasecmp("little",str_endian,6)) endian = false;
          else if (!cimg::strncasecmp("big",str_endian,3)) endian = true;
          nn1 = n1==~0U?N - 1:n1;
          if (nn1>=N)
            throw CImgArgumentException(_cimglist_instance
                                        "load_cimg(): Invalid specified coordinates [%u](%u,%u,%u,%u) -> [%u](%u,%u,%u,%u) "
                                        "because file '%s' contains only %u images.",
                                        cimglist_instance,
                                        n0,x0,y0,z0,c0,n1,x1,y1,z1,c1,filename?filename:"(FILE*)",N);
          assign(1 + nn1 - n0);
          _cimg_load_cimg_case2("bool",bool);
          _cimg_load_cimg_case2("unsigned_char",unsigned char);
          _cimg_load_cimg_case2("uchar",unsigned char);
          _cimg_load_cimg_case2("char",char);
          _cimg_load_cimg_case2("unsigned_short",unsigned short);
          _cimg_load_cimg_case2("ushort",unsigned short);
          _cimg_load_cimg_case2("short",short);
          _cimg_load_cimg_case2("unsigned_int",unsigned int);
          _cimg_load_cimg_case2("uint",unsigned int);
          _cimg_load_cimg_case2("int",int);
          _cimg_load_cimg_case2("unsigned_long",ulongT);
          _cimg_load_cimg_case2("ulong",ulongT);
          _cimg_load_cimg_case2("long",longT);
          _cimg_load_cimg_case2("unsigned_int64",uint64T);
          _cimg_load_cimg_case2("uint64",uint64T);
          _cimg_load_cimg_case2("int64",int64T);
          _cimg_load_cimg_case2("float",float);
          _cimg_load_cimg_case2("double",double);
          if (!loaded) {
            if (!file) cimg::fclose(nfile);
            throw CImgIOException(_cimglist_instance
                                  "load_cimg(): Unsupported pixel type '%s' for file '%s'.",
                                  cimglist_instance,
                                  str_pixeltype._data,filename?filename:"(FILE*)");
          }
          if (!file) cimg::fclose(nfile);
          return *this;
        }
    
        //! Load a list from a PAR/REC (Philips) file.
        /**
          \param filename Filename to read data from.
        **/
        CImgList<T>& load_parrec(const char *const filename) {
          if (!filename)
            throw CImgArgumentException(_cimglist_instance
                                        "load_parrec(): Specified filename is (null).",
                                        cimglist_instance);
    
          CImg<charT> body(1024), filenamepar(1024), filenamerec(1024);
          *body = *filenamepar = *filenamerec = 0;
          const char *const ext = cimg::split_filename(filename,body);
          if (!std::strcmp(ext,"par")) {
            std::strncpy(filenamepar,filename,filenamepar._width - 1);
            cimg_snprintf(filenamerec,filenamerec._width,"%s.rec",body._data);
          }
          if (!std::strcmp(ext,"PAR")) {
            std::strncpy(filenamepar,filename,filenamepar._width - 1);
            cimg_snprintf(filenamerec,filenamerec._width,"%s.REC",body._data);
          }
          if (!std::strcmp(ext,"rec")) {
            std::strncpy(filenamerec,filename,filenamerec._width - 1);
            cimg_snprintf(filenamepar,filenamepar._width,"%s.par",body._data);
          }
          if (!std::strcmp(ext,"REC")) {
            std::strncpy(filenamerec,filename,filenamerec._width - 1);
            cimg_snprintf(filenamepar,filenamepar._width,"%s.PAR",body._data);
          }
          std::FILE *file = cimg::fopen(filenamepar,"r");
    
          // Parse header file
          CImgList<floatT> st_slices;
          CImgList<uintT> st_global;
          CImg<charT> line(256); *line = 0;
          int err;
          do { err = std::fscanf(file,"%255[^\n]%*c",line._data); } while (err!=EOF && (*line=='#' || *line=='.'));
          do {
            unsigned int sn,size_x,size_y,pixsize;
            float rs,ri,ss;
            err = std::fscanf(file,"%u%*u%*u%*u%*u%*u%*u%u%*u%u%u%g%g%g%*[^\n]",&sn,&pixsize,&size_x,&size_y,&ri,&rs,&ss);
            if (err==7) {
              CImg<floatT>::vector((float)sn,(float)pixsize,(float)size_x,(float)size_y,ri,rs,ss,0).move_to(st_slices);
              unsigned int i; for (i = 0; i<st_global._width && sn<=st_global[i][2]; ++i) {}
              if (i==st_global._width) CImg<uintT>::vector(size_x,size_y,sn).move_to(st_global);
              else {
                CImg<uintT> &vec = st_global[i];
                if (size_x>vec[0]) vec[0] = size_x;
                if (size_y>vec[1]) vec[1] = size_y;
                vec[2] = sn;
              }
              st_slices[st_slices._width - 1][7] = (float)i;
            }
          } while (err==7);
    
          // Read data
          std::FILE *file2 = cimg::fopen(filenamerec,"rb");
          cimglist_for(st_global,l) {
            const CImg<uintT>& vec = st_global[l];
            CImg<T>(vec[0],vec[1],vec[2]).move_to(*this);
          }
    
          cimglist_for(st_slices,l) {
            const CImg<floatT>& vec = st_slices[l];
            const unsigned int
              sn = (unsigned int)vec[0] - 1,
              pixsize = (unsigned int)vec[1],
              size_x = (unsigned int)vec[2],
              size_y = (unsigned int)vec[3],