int handle_decode(char **argv, Images &images, int quality, int scale) { char *ext = strrchr(argv[1],'.'); if (!check_compatible_extension(ext)) { e_printf("Error: expected \".png\", \".pnm\" or \".pam\" file name extension for output file\n"); return 1; } const auto tim0 = high_resolution_clock::now(); if (!decode_flif(argv, images, quality, scale)) {e_printf("Error: could not decode FLIF file\n"); return 3; } const auto tim1 = high_resolution_clock::now(); printf("decoded in %lld msec\n", duration_cast<milliseconds>(tim1 - tim0).count()); if (scale>1) v_printf(3,"Downscaling output: %ux%u -> %ux%u\n",images[0].cols(),images[0].rows(),images[0].cols()/scale,images[0].rows()/scale); if (images.size() == 1) { if (!images[0].save(argv[1],scale)) return 2; } else { int counter=0; std::vector<char> vfilename(strlen(argv[1])+6); char *filename = &vfilename[0]; strcpy(filename,argv[1]); char *a_ext = strrchr(filename,'.'); for (Image& image : images) { sprintf(a_ext,"-%03d%s",counter++,ext); if (!image.save(filename,scale)) return 2; v_printf(2," (%i/%i) \r",counter,(int)images.size()); v_printf(4,"\n"); } } v_printf(2,"\n"); return 0; }
bool process(const ColorRanges *srcRanges, const Images &images) { int np=srcRanges->numPlanes(); nb = images.size(); seen_before.clear(); seen_before.resize(nb,-1); bool dupes_found=false; for (unsigned int fr=1; fr<images.size(); fr++) { const Image& image = images[fr]; for (unsigned int ofr=0; ofr<fr; ofr++) { const Image& oimage = images[ofr]; bool identical=true; for (uint32_t r=0; r<image.rows(); r++) { for (uint32_t c=0; c<image.cols(); c++) { for (int p=0; p<np; p++) { if(image(p,r,c) != oimage(p,r,c)) { identical=false; break;} } if (!identical) {break;} } if (!identical) {break;} } if (identical) {seen_before[fr] = ofr; dupes_found=true; break;} } } return dupes_found; }
int handle_decode(char **argv, Images &images, int quality, int scale) { char *ext = strrchr(argv[1],'.'); if (!check_compatible_extension(ext)) { e_printf("Error: expected \".png\", \".pnm\" or \".pam\" file name extension for output file\n"); return 1; } if (!decode_flif(argv, images, quality, scale)) return 3; if (scale>1) v_printf(3,"Downscaling output: %ux%u -> %ux%u\n",images[0].cols(),images[0].rows(),images[0].cols()/scale,images[0].rows()/scale); if (images.size() == 1) { if (!images[0].save(argv[1],scale)) return 2; } else { int counter=0; std::vector<char> vfilename(strlen(argv[1])+6); char *filename = &vfilename[0]; strcpy(filename,argv[1]); char *a_ext = strrchr(filename,'.'); for (Image& image : images) { sprintf(a_ext,"-%03d%s",counter++,ext); if (!image.save(filename,scale)) return 2; v_printf(2," (%i/%i) \r",counter,(int)images.size()); v_printf(4,"\n"); } } v_printf(2,"\n"); return -1; }
bool encode_load_input_images(int argc, char **argv, Images &images) { int nb_input_images = argc-1; while(argc>1) { Image image; v_printf(2,"\r"); if (!image.load(argv[0])) { e_printf("Could not read input file: %s\n", argv[0]); return false; }; images.push_back(std::move(image)); Image& last_image = images.back(); if (last_image.rows() != images[0].rows() || last_image.cols() != images[0].cols()) { e_printf("Dimensions of all input images should be the same!\n"); e_printf(" First image is %ux%u\n",images[0].cols(),images[0].rows()); e_printf(" This image is %ux%u: %s\n",last_image.cols(),last_image.rows(),argv[0]); return false; } if (last_image.numPlanes() < images[0].numPlanes()) { if (images[0].numPlanes() == 3) last_image.ensure_chroma(); else if (images[0].numPlanes() == 4) last_image.ensure_alpha(); else { e_printf("Problem while loading input images, please report this.\n"); return false; } } else if (last_image.numPlanes() > images[0].numPlanes()) { if (last_image.numPlanes() == 3) { for (Image& i : images) i.ensure_chroma(); } else if (last_image.numPlanes() == 4) { for (Image& i : images) i.ensure_alpha(); } else { e_printf("Problem while loading input images, please report this.\n"); return false; } } argc--; argv++; if (nb_input_images>1) {v_printf(2," (%i/%i) ",(int)images.size(),nb_input_images); v_printf(4,"\n");} } v_printf(2,"\n"); return true; }
template<typename Coder> void flif_decode_scanlines_inner(std::vector<Coder> &coders, Images &images, const ColorRanges *ranges) { ColorVal min,max; int nump = images[0].numPlanes(); for (int k=0,i=0; k < 5; k++) { int p=PLANE_ORDERING[k]; if (p>=nump) continue; i++; Properties properties((nump>3?NB_PROPERTIES_scanlinesA[p]:NB_PROPERTIES_scanlines[p])); v_printf(2,"\r%i%% done [%i/%i] DEC[%ux%u] ",(int)(100*pixels_done/pixels_todo),i,nump,images[0].cols(),images[0].rows()); v_printf(4,"\n"); pixels_done += images[0].cols()*images[0].rows(); if (ranges->min(p) >= ranges->max(p)) continue; for (uint32_t r = 0; r < images[0].rows(); r++) { for (int fr=0; fr< (int)images.size(); fr++) { Image& image = images[fr]; uint32_t begin=image.col_begin[r], end=image.col_end[r]; if (image.seen_before >= 0) { for(uint32_t c=0; c<image.cols(); c++) image.set(p,r,c,images[image.seen_before](p,r,c)); continue; } if (fr>0) { for (uint32_t c = 0; c < begin; c++) if (nump>3 && p<3 && image(3,r,c) == 0) image.set(p,r,c,predict_and_calcProps_scanlines(properties,ranges,image,p,r,c,min,max)); else if (p !=4 ) image.set(p,r,c,images[fr-1](p,r,c)); /* else if (nump>4 && p<4 && image(4,r,c) > 0) image.set(p,r,c,images[fr-image(4,r,c)](p,r,c)); else { int oldframe=fr-1; image.set(p,r,c,images[oldframe](p,r,c)); while(p == 4 && image(p,r,c) > 0) {oldframe -= image(p,r,c); assert(oldframe>=0); image.set(p,r,c,images[oldframe](p,r,c));} } */ } else { if (nump>3 && p<3) { begin=0; end=image.cols(); } } for (uint32_t c = begin; c < end; c++) { ColorVal guess = predict_and_calcProps_scanlines(properties,ranges,image,p,r,c,min,max); if (p==4 && max > fr) max = fr; if (nump>3 && p<3 && image(3,r,c) == 0) {image.set(p,r,c,guess); continue;} if (nump>4 && p<4 && image(4,r,c) > 0) {image.set(p,r,c,images[fr-image(4,r,c)](p,r,c)); continue;} ColorVal curr = coders[p].read_int(properties, min - guess, max - guess) + guess; image.set(p,r,c, curr); } if (fr>0) { for (uint32_t c = end; c < image.cols(); c++) if (nump>3 && p<3 && image(3,r,c) == 0) image.set(p,r,c,predict_and_calcProps_scanlines(properties,ranges,image,p,r,c,min,max)); else if (p !=4 ) image.set(p,r,c,images[fr-1](p,r,c)); /* else if (nump>4 && p<4 && image(4,r,c) > 0) image.set(p,r,c,images[fr-image(4,r,c)](p,r,c)); else { int oldframe=fr-1; image.set(p,r,c,images[oldframe](p,r,c)); while(p == 4 && image(p,r,c) > 0) {oldframe -= image(p,r,c); assert(oldframe>=0); image.set(p,r,c,images[oldframe](p,r,c));} }*/ } } } } }
bool encode_flif(int argc, char **argv, Images &images, int palette_size, int acb, flifEncodingOptional method, int lookback, int learn_repeats, int frame_delay, int divisor=CONTEXT_TREE_COUNT_DIV, int min_size=CONTEXT_TREE_MIN_SUBTREE_SIZE, int split_threshold=CONTEXT_TREE_SPLIT_THRESHOLD, int yiq=1, int plc=1) { bool flat=true; for (Image &image : images) if (image.uses_alpha()) flat=false; if (flat && images[0].numPlanes() == 4) { v_printf(2,"Alpha channel not actually used, dropping it.\n"); for (Image &image : images) image.drop_alpha(); } bool grayscale=true; for (Image &image : images) if (image.uses_color()) grayscale=false; if (grayscale && images[0].numPlanes() == 3) { v_printf(2,"Chroma not actually used, dropping it.\n"); for (Image &image : images) image.drop_color(); } uint64_t nb_pixels = (uint64_t)images[0].rows() * images[0].cols(); std::vector<std::string> desc; if (nb_pixels > 2) { // no point in doing anything for 1- or 2-pixel images if (plc) desc.push_back("PLC"); // compactify channels if (yiq) desc.push_back("YIQ"); // convert RGB(A) to YIQ(A) desc.push_back("BND"); // get the bounds of the color spaces } if (palette_size < 0) { palette_size = 1024; if (nb_pixels * images.size() / 2 < 1024) palette_size = nb_pixels * images.size() / 2; } if (palette_size > 0) desc.push_back("PLA"); // try palette (including alpha) if (palette_size > 0) desc.push_back("PLT"); // try palette (without alpha) if (acb == -1) { // not specified if ACB should be used if (nb_pixels * images.size() > 10000) desc.push_back("ACB"); // try auto color buckets on large images } else if (acb) desc.push_back("ACB"); // try auto color buckets if forced if (method.o == Optional::undefined) { // no method specified, pick one heuristically if (nb_pixels * images.size() < 10000) method.encoding=flifEncoding::nonInterlaced; // if the image is small, not much point in doing interlacing else method.encoding=flifEncoding::interlaced; // default method: interlacing } if (images.size() > 1) { desc.push_back("DUP"); // find duplicate frames desc.push_back("FRS"); // get the shapes of the frames if (lookback != 0) desc.push_back("FRA"); // make a "deep" alpha channel (negative values are transparent to some previous frame) } if (learn_repeats < 0) { // no number of repeats specified, pick a number heuristically learn_repeats = TREE_LEARN_REPEATS; if (nb_pixels * images.size() < 5000) learn_repeats--; // avoid large trees for small images if (learn_repeats < 0) learn_repeats=0; } FILE *file = fopen(argv[0],"wb"); if (!file) return false; FileIO fio(file, argv[0]); return flif_encode(fio, images, desc, method.encoding, learn_repeats, acb, frame_delay, palette_size, lookback, divisor, min_size, split_threshold); }
bool process(const ColorRanges *srcRanges, const Images &images) { if (images.size()<2) return false; int np=srcRanges->numPlanes(); nb = 0; cols = images[0].cols(); for (unsigned int fr=1; fr<images.size(); fr++) { const Image& image = images[fr]; if (image.seen_before >= 0) continue; nb += image.rows(); for (uint32_t r=0; r<image.rows(); r++) { bool beginfound=false; for (uint32_t c=0; c<image.cols(); c++) { if (image.alpha_zero_special && np>3 && image(3,r,c) == 0 && images[fr-1](3,r,c)==0) continue; for (int p=0; p<np; p++) { if(image(p,r,c) != images[fr-1](p,r,c)) { beginfound=true; break;} } if (beginfound) {b.push_back(c); break;} } if (!beginfound) {b.push_back(image.cols()); e.push_back(image.cols()); continue;} bool endfound=false; for (uint32_t c=image.cols()-1; c >= b.back(); c--) { if (image.alpha_zero_special && np>3 && image(3,r,c) == 0 && images[fr-1](3,r,c)==0) continue; for (int p=0; p<np; p++) { if(image(p,r,c) != images[fr-1](p,r,c)) { endfound=true; break;} } if (endfound) {e.push_back(c+1); break;} } if (!endfound) {e.push_back(0); continue;} //shouldn't happen, right? } } /* does not seem to do much good at all if (nb&1) {b.push_back(b[nb-1]); e.push_back(e[nb-1]);} for (unsigned int i=0; i<nb; i+=2) { b[i]=b[i+1]=std::min(b[i],b[i+1]); } for (unsigned int i=0; i<nb; i+=2) { e[i]=e[i+1]=std::max(e[i],e[i+1]); } */ return true; }
int handle_decode(int argc, char **argv, Images &images, int quality, int scale, int resize_width, int resize_height) { if (scale < 0) { // just identify the file(s), don't actually decode while (argc>0) { decode_flif(argv, images, quality, scale, resize_width, resize_height); argv++; argc--; } return 0; } char *ext = strrchr(argv[1],'.'); if (!check_compatible_extension(ext) && strcmp(argv[1],"null:")) { e_printf("Error: expected \".png\", \".pnm\" or \".pam\" file name extension for output file\n"); return 1; } if (!decode_flif(argv, images, quality, scale, resize_width, resize_height)) { e_printf("Error: could not decode FLIF file\n"); return 3; } if (!strcmp(argv[1],"null:")) return 0; // if (scale>1) // v_printf(3,"Downscaling output: %ux%u -> %ux%u\n",images[0].cols(),images[0].rows(),images[0].cols()/scale,images[0].rows()/scale); if (images.size() == 1) { if (!images[0].save(argv[1])) return 2; } else { int counter=0; std::vector<char> vfilename(strlen(argv[1])+6); char *filename = &vfilename[0]; strcpy(filename,argv[1]); char *a_ext = strrchr(filename,'.'); for (Image& image : images) { sprintf(a_ext,"-%03d%s",counter++,ext); if (!image.save(filename)) return 2; v_printf(2," (%i/%i) \r",counter,(int)images.size()); v_printf(4,"\n"); } } v_printf(2,"\n"); return 0; }
void Enhancer::analyze(void) { for (int i=0; i<p_files->size(); i++) { Images *images = p_files->at(i)->load_images(); for (int idx=0; idx<images->size(); idx++) { Image *img = images->at(idx); //img->gen_preview(); //img->gen_thumbnail(); img->analyse(&m_filterQueue); //img->free_pix(); p_images->push_back(img); } delete images; std::cout << i << std::endl; } }
bool process(const ColorRanges *srcRanges, const Images &images) { std::vector<ColorVal> pixel(images[0].numPlanes()); // fill buckets for (const Image& image : images) for (uint32_t r=0; r<image.rows(); r++) { for (uint32_t c=0; c<image.cols(); c++) { int p; for (p=0; p<image.numPlanes(); p++) { ColorVal v = image(p,r,c); pixel[p] = v; } if (image.alpha_zero_special && p>3 && pixel[3]==0) { cb->findBucket(3, pixel).addColor(0,max_per_colorbucket[3]); continue;} cb->addColor(pixel); } } cb->bucket0.simplify_lossless(); cb->bucket3.simplify_lossless(); for (auto& b : cb->bucket1) b.simplify_lossless(); for (auto& bv : cb->bucket2) for (auto& b : bv) b.simplify_lossless(); // TODO: IMPROVE THESE HEURISTICS! // TAKE IMAGE SIZE INTO ACCOUNT! // CONSIDER RELATIVE AREA OF BUCKETS / BOUNDS! // printf("Filled color buckets with %i discrete colors + %i continous buckets\n",totaldiscretecolors,totalcontinuousbuckets); int64_t total_pixels = (int64_t) images.size() * images[0].rows() * images[0].cols(); v_printf(7,", [D=%i,C=%i,P=%i]",totaldiscretecolors,totalcontinuousbuckets,(int) (total_pixels/100)); if (totaldiscretecolors < total_pixels/200 && totalcontinuousbuckets < total_pixels/50) return true; if (totaldiscretecolors < total_pixels/100 && totalcontinuousbuckets < total_pixels/200) return true; if (totaldiscretecolors < total_pixels/40 && totalcontinuousbuckets < total_pixels/500) return true; // simplify buckets for (int factor = 95; factor >= 35; factor -= 10) { for (auto& b : cb->bucket1) b.simplify(factor); for (auto& bv : cb->bucket2) for (auto& b : bv) b.simplify(factor-20); v_printf(8,"->[D=%i,C=%i]",totaldiscretecolors,totalcontinuousbuckets); if (totaldiscretecolors < total_pixels/200 && totalcontinuousbuckets < total_pixels/100) return true; } return false; }
// a heuristic to figure out if this is going to help (it won't help if we introduce more entropy than what is eliminated) bool process(const ColorRanges *srcRanges, const Images &images) { if (images.size() < 2) return false; int nump=images[0].numPlanes(); nb_frames = images.size(); int64_t pixel_cost = 1; for (int p=0; p<nump; p++) pixel_cost *= (1 + srcRanges->max(p) - srcRanges->min(p)); // pixel_cost is roughly the cost per pixel (number of different values a pixel can take) if (pixel_cost < 16) {v_printf(7,", no_FRA[pixels_too_cheap:%i]", pixel_cost); return false;} // pixels are too cheap, no point in trying to save stuff std::vector<uint64_t> found_pixels(images.size(), 0); uint64_t new_pixels=0; max_lookback=1; if (user_max_lookback == -1) user_max_lookback = images.size()-1; for (int fr=1; fr < (int)images.size(); fr++) { const Image& image = images[fr]; for (uint32_t r=0; r<image.rows(); r++) { for (uint32_t c=image.col_begin[r]; c<image.col_end[r]; c++) { new_pixels++; for (int prev=1; prev <= fr; prev++) { if (prev>user_max_lookback) break; bool identical=true; if (image.alpha_zero_special && nump>3 && image(3,r,c) == 0 && images[fr-prev](3,r,c) == 0) identical=true; else for (int p=0; p<nump; p++) { if(image(p,r,c) != images[fr-prev](p,r,c)) { identical=false; break;} } if (identical) { found_pixels[prev]++; new_pixels--; if (prev>max_lookback) max_lookback=prev; break;} } } } } if (images.size() > 2) v_printf(7,", trying_FRA(at -1: %llu, at -2: %llu, new: %llu)",(long long unsigned) found_pixels[1],(long long unsigned) found_pixels[2], (long long unsigned) new_pixels); if (max_lookback>256) max_lookback=256; for(int i=1; i <= max_lookback; i++) { v_printf(8,"at lookback %i: %llu pixels\n",-i, found_pixels[i]); if (found_pixels[i] <= new_pixels/200 || i>pixel_cost) {max_lookback=i-1; break;} found_pixels[0] += found_pixels[i]; } for(int i=max_lookback+1; i<(int)images.size(); i++) { if (found_pixels[i] > new_pixels/200 && i<pixel_cost) {max_lookback=i; found_pixels[0] += found_pixels[i];} else new_pixels += found_pixels[i]; } return (found_pixels[0] * pixel_cost > new_pixels * (2 + max_lookback)); };
void data(Images &images) const { for (int fr=1; fr < (int)images.size(); fr++) { uint32_t ipixels=0; Image& image = images[fr]; for (uint32_t r=0; r<image.rows(); r++) { for (uint32_t c=image.col_begin[r]; c<image.col_end[r]; c++) { for (int prev=1; prev <= fr; prev++) { if (prev>max_lookback) break; bool identical=true; if (image.alpha_zero_special && image(3,r,c) == 0 && images[fr-prev](3,r,c) == 0) identical=true; else for (int p=0; p<4; p++) { if(image(p,r,c) != images[fr-prev](p,r,c)) { identical=false; break;} } if (identical) {image.set(4,r,c, prev); ipixels++; break;} } } } // printf("frame %i: found %u pixels from previous frames\n", fr, ipixels); } }
bool encode_load_input_images(int argc, char **argv, Images &images) { int nb_input_images = argc-1; while(argc>1) { Image image; v_printf(2,"\r"); if (!image.load(argv[0])) { e_printf("Could not read input file: %s\n", argv[0]); return false; }; images.push_back(std::move(image)); const Image& last_image = images.back(); if (last_image.rows() != images[0].rows() || last_image.cols() != images[0].cols() || last_image.numPlanes() != images[0].numPlanes()) { e_printf("Dimensions of all input images should be the same!\n"); e_printf(" First image is %ux%u, %i channels.\n",images[0].cols(),images[0].rows(),images[0].numPlanes()); e_printf(" This image is %ux%u, %i channels: %s\n",last_image.cols(),last_image.rows(),last_image.numPlanes(),argv[0]); return false; } argc--; argv++; if (nb_input_images>1) {v_printf(2," (%i/%i) ",(int)images.size(),nb_input_images); v_printf(4,"\n");} } v_printf(2,"\n"); return true; }
bool handle_encode_arguments(int argc, char **argv, Images &images, int palette_size, int acb, flifEncodingOptional method, int lookback, int learn_repeats, int frame_delay) { int nb_input_images = argc-1; while(argc>1) { Image image; v_printf(2,"\r"); if (!image.load(argv[0])) { e_printf("Could not read input file: %s\n", argv[0]); return 2; }; images.push_back(std::move(image)); const Image& last_image = images.back(); if (last_image.rows() != images[0].rows() || last_image.cols() != images[0].cols() || last_image.numPlanes() != images[0].numPlanes()) { e_printf("Dimensions of all input images should be the same!\n"); e_printf(" First image is %ux%u, %i channels.\n",images[0].cols(),images[0].rows(),images[0].numPlanes()); e_printf(" This image is %ux%u, %i channels: %s\n",last_image.cols(),last_image.rows(),last_image.numPlanes(),argv[0]); return 2; } argc--; argv++; if (nb_input_images>1) {v_printf(2," (%i/%i) ",(int)images.size(),nb_input_images); v_printf(4,"\n");} } v_printf(2,"\n"); bool flat=true; for (Image &image : images) if (image.uses_alpha()) flat=false; if (flat && images[0].numPlanes() == 4) { v_printf(2,"Alpha channel not actually used, dropping it.\n"); for (Image &image : images) image.drop_alpha(); } uint64_t nb_pixels = (uint64_t)images[0].rows() * images[0].cols(); std::vector<std::string> desc; desc.push_back("YIQ"); // convert RGB(A) to YIQ(A) desc.push_back("BND"); // get the bounds of the color spaces if (palette_size > 0) desc.push_back("PLA"); // try palette (including alpha) if (palette_size > 0) desc.push_back("PLT"); // try palette (without alpha) if (acb == -1) { // not specified if ACB should be used if (nb_pixels > 10000) desc.push_back("ACB"); // try auto color buckets on large images } else if (acb) desc.push_back("ACB"); // try auto color buckets if forced if (method.o == Optional::undefined) { // no method specified, pick one heuristically if (nb_pixels < 10000) method.encoding=flifEncoding::nonInterlaced; // if the image is small, not much point in doing interlacing else method.encoding=flifEncoding::interlaced; // default method: interlacing } if (images.size() > 1) { desc.push_back("DUP"); // find duplicate frames desc.push_back("FRS"); // get the shapes of the frames if (lookback != 0) desc.push_back("FRA"); // make a "deep" alpha channel (negative values are transparent to some previous frame) } if (learn_repeats < 0) { // no number of repeats specified, pick a number heuristically learn_repeats = TREE_LEARN_REPEATS; if (nb_pixels < 5000) learn_repeats--; // avoid large trees for small images if (learn_repeats < 0) learn_repeats=0; } FILE *file = fopen(argv[0],"wb"); if (!file) return false; FileIO fio(file, argv[0]); return flif_encode(fio, images, desc, method.encoding, learn_repeats, acb, frame_delay, palette_size, lookback); }
bool flif_decode(IO& io, Images &images, int quality, int scale, uint32_t (*callback)(int,int), Images &partial_images) { if (scale != 1 && scale != 2 && scale != 4 && scale != 8 && scale != 16 && scale != 32 && scale != 64 && scale != 128) { e_printf("Invalid scale down factor: %i\n", scale); return false; } char buff[5]; if (!io.gets(buff,5)) { e_printf("Could not read header from file: %s\n",io.getName()); return false; } if (!strcmp(buff,"!<ar")) { // FLIF file in an archive, try to find find the main image if (!io.gets(buff,5)) return false; if (strcmp(buff,"ch>\n")) return false; char ar_header[61]; while (true) { if (!io.gets(ar_header,61)) { e_printf("Archive does not contain a FLIF image\n"); return false; } if (!strncmp(ar_header,"__image.flif/",13)) { if (!io.gets(buff,5)) { e_printf("Corrupt archive?\n"); return false; } break; } else { long skip = strtol(&ar_header[48],NULL,10); if (skip < 0) return false; if (skip & 1) skip++; io.fseek(skip,SEEK_CUR); } } } if (strcmp(buff,"FLIF")) { e_printf("Not a FLIF file: %s (header: \"%s\")\n",io.getName(),buff); return false; } int c = io.getc(); if (c < ' ' || c > ' '+32+15+32) { e_printf("Invalid or unknown FLIF format byte\n"); return false;} c -= ' '; int numFrames=1; if (c > 47) { c -= 32; numFrames = io.getc(); if (numFrames < 2 || numFrames >= 255) return false; } const int encoding=c/16; if (encoding < 1 || encoding > 2) { e_printf("Invalid or unknown FLIF encoding method\n"); return false;} if (scale != 1 && encoding==1) { v_printf(1,"Cannot decode non-interlaced FLIF file at lower scale! Ignoring scale...\n");} if (quality < 100 && encoding==1) { v_printf(1,"Cannot decode non-interlaced FLIF file at lower quality! Ignoring quality...\n");} int numPlanes=c%16; if (numPlanes < 1 || numPlanes > 4) {e_printf("Invalid FLIF header (unsupported color channels)\n"); return false;} c = io.getc(); if (c < '0' || c > '2') {e_printf("Invalid FLIF header (unsupported color depth)\n"); return false;} int width=io.getc() << 8; width += io.getc(); int height=io.getc() << 8; height += io.getc(); if (width < 1 || height < 1) {e_printf("Invalid FLIF header\n"); return false;} // TODO: implement downscaled decoding without allocating a fullscale image buffer! RacIn<IO> rac(io); SimpleSymbolCoder<FLIFBitChanceMeta, RacIn<IO>, 24> metaCoder(rac); // image.init(width, height, 0, 0, 0); v_printf(3,"Decoding %ux%u image, channels:",width,height); int maxmax=0; for (int p = 0; p < numPlanes; p++) { // int min = 0; int max = 255; if (c=='2') max=65535; else if (c=='0') max=(1 << metaCoder.read_int(1, 16)) - 1; if (max>maxmax) maxmax=max; // image.add_plane(min, max); // v_printf(2," [%i] %i bpp (%i..%i)",p,ilog2(image.max(p)+1),image.min(p), image.max(p)); if (c=='0') v_printf(3," [%i] %i bpp",p,ilog2(max+1)); } if (c=='1') v_printf(3," %i, depth: 8 bit",numPlanes); if (c=='2') v_printf(3," %i, depth: 16 bit",numPlanes); if (numFrames>1) v_printf(3,", frames: %i",numFrames); v_printf(3,"\n"); if (numFrames>1) { // ignored for now (assuming loop forever) metaCoder.read_int(0, 100); // repeats (0=infinite) } for (int i=0; i<numFrames; i++) { images.push_back(Image()); if (!images[i].init(width,height,0,maxmax,numPlanes)) return false; if (numFrames>1) images[i].frame_delay = metaCoder.read_int(0, 60000); // time in ms between frames if (callback) partial_images.push_back(Image()); //if (numFrames>1) partial_images[i].frame_delay = images[i].frame_delay; } std::vector<const ColorRanges*> rangesList; std::vector<Transform<IO>*> transforms; rangesList.push_back(getRanges(images[0])); v_printf(4,"Transforms: "); int tcount=0; transform_l=0; while (rac.read()) { if (transform_l > MAX_TRANSFORM) return false; std::string desc = read_name(rac); Transform<IO> *trans = create_transform<IO>(desc); if (!trans) { e_printf("Unknown transformation '%s'\n", desc.c_str()); return false; } if (!trans->init(rangesList.back())) { e_printf("Transformation '%s' failed\n", desc.c_str()); return false; } if (tcount++ > 0) v_printf(4,", "); v_printf(4,"%s", desc.c_str()); if (desc == "FRA" && images.size()<2) return false; if (desc == "FRS") { if (images.size()<2) return false; int unique_frames=images.size()-1; // not considering first frame for (Image& i : images) if (i.seen_before >= 0) unique_frames--; if (unique_frames < 1) {return false;} trans->configure(unique_frames*images[0].rows()); trans->configure(images[0].cols()); } if (desc == "DUP") { if (images.size()<2) return false; else trans->configure(images.size()); } if (!trans->load(rangesList.back(), rac)) return false; rangesList.push_back(trans->meta(images, rangesList.back())); transforms.push_back(trans); } if (tcount==0) v_printf(4,"none\n"); else v_printf(4,"\n"); const ColorRanges* ranges = rangesList.back(); grey.clear(); for (int p = 0; p < ranges->numPlanes(); p++) grey.push_back((ranges->min(p)+ranges->max(p))/2); pixels_todo = (int64_t)width*height*ranges->numPlanes()/scale/scale; pixels_done = 0; for (int p = 0; p < ranges->numPlanes(); p++) { v_printf(7,"Plane %i: %i..%i\n",p,ranges->min(p),ranges->max(p)); } for (int p = 0; p < ranges->numPlanes(); p++) { if (ranges->min(p) >= ranges->max(p)) { v_printf(4,"Constant plane %i at color value %i\n",p,ranges->min(p)); //for (ColorVal_intern& x : image(p).data) x=ranges->min(p); for (int fr = 0; fr < numFrames; fr++) for (uint32_t r=0; r<images[fr].rows(); r++) { for (uint32_t c=0; c<images[fr].cols(); c++) { images[fr].set(p,r,c,ranges->min(p)); } } } } int mbits = 0; for (int p = 0; p < ranges->numPlanes(); p++) { if (ranges->max(p) > ranges->min(p)) { int nBits = ilog2((ranges->max(p) - ranges->min(p))*2-1)+1; if (nBits > mbits) mbits = nBits; } } int bits = 10; #ifdef SUPPORT_HDR if (mbits >10) bits=18; #endif if (mbits > bits) { e_printf("This FLIF cannot decode >8 bit per channel files. Please compile with SUPPORT_HDR.\n"); return false;} std::vector<Tree> forest(ranges->numPlanes(), Tree()); int roughZL = 0; if (encoding == 2) { roughZL = images[0].zooms() - NB_NOLEARN_ZOOMS-1; if (roughZL < 0) roughZL = 0; // v_printf(2,"Decoding rough data\n"); if (bits==10) flif_decode_FLIF2_pass<IO, RacIn<IO>, FinalPropertySymbolCoder<FLIFBitChancePass2, RacIn<IO>, 10> >(rac, images, ranges, forest, images[0].zooms(), roughZL+1, 100, scale, transforms, callback, partial_images); #ifdef SUPPORT_HDR else flif_decode_FLIF2_pass<IO, RacIn<IO>, FinalPropertySymbolCoder<FLIFBitChancePass2, RacIn<IO>, 18> >(rac, images, ranges, forest, images[0].zooms(), roughZL+1, 100, scale, transforms, callback, partial_images); #endif } if (encoding == 2 && quality <= 0) { v_printf(3,"Not decoding MANIAC tree\n"); } else { v_printf(3,"Decoded header + rough data. Decoding MANIAC tree.\n"); if (!flif_decode_tree<FLIFBitChanceTree, RacIn<IO>>(rac, ranges, forest, encoding)) return false; } switch(encoding) { case 1: v_printf(3,"Decoding data (scanlines)\n"); if (bits==10) flif_decode_scanlines_pass<IO, RacIn<IO>, FinalPropertySymbolCoder<FLIFBitChancePass2, RacIn<IO>, 10> >(rac, images, ranges, forest, transforms, callback, partial_images); #ifdef SUPPORT_HDR else flif_decode_scanlines_pass<IO, RacIn<IO>, FinalPropertySymbolCoder<FLIFBitChancePass2, RacIn<IO>, 18> >(rac, images, ranges, forest, transforms, callback, partial_images); #endif break; case 2: v_printf(3,"Decoding data (interlaced)\n"); if (bits==10) flif_decode_FLIF2_pass<IO, RacIn<IO>, FinalPropertySymbolCoder<FLIFBitChancePass2, RacIn<IO>, 10> >(rac, images, ranges, forest, roughZL, 0, quality, scale, transforms, callback, partial_images); #ifdef SUPPORT_HDR else flif_decode_FLIF2_pass<IO, RacIn<IO>, FinalPropertySymbolCoder<FLIFBitChancePass2, RacIn<IO>, 18> >(rac, images, ranges, forest, roughZL, 0, quality, scale, transforms, callback, partial_images); #endif break; } if (quality==100 && scale==1) { uint32_t checksum = images[0].checksum(); v_printf(8,"Computed checksum: %X\n", checksum); uint32_t checksum2 = metaCoder.read_int(0, 0xFFFF); checksum2 *= 0x10000; checksum2 += metaCoder.read_int(0, 0xFFFF); v_printf(8,"Read checksum: %X\n", checksum2); if (checksum != checksum2) v_printf(1,"\nCORRUPTION DETECTED! (partial file?)\n\n"); else v_printf(2,"Image decoded, checksum verified.\n"); } else { v_printf(2,"Not checking checksum, lossy partial decoding was chosen.\n"); } if (numFrames==1) v_printf(2,"\rDecoding done, %li bytes for %ux%u pixels (%.4fbpp) \n",rac.ftell(), images[0].cols()/scale, images[0].rows()/scale, 8.0*rac.ftell()/images[0].rows()/images[0].cols()/scale/scale); else v_printf(2,"\rDecoding done, %li bytes for %i frames of %ux%u pixels (%.4fbpp) \n",rac.ftell(), numFrames, images[0].cols()/scale, images[0].rows()/scale, 8.0*rac.ftell()/numFrames/images[0].rows()/images[0].cols()/scale/scale); for (int i=(int)transforms.size()-1; i>=0; i--) { transforms[i]->invData(images); delete transforms[i]; } transforms.clear(); for (unsigned int i=0; i<rangesList.size(); i++) { delete rangesList[i]; } rangesList.clear(); return true; }
template<typename IO, typename Rac, typename Coder> void flif_decode_scanlines_inner(Rac &rac, std::vector<Coder> &coders, Images &images, const ColorRanges *ranges, std::vector<Transform<IO>*> &transforms, uint32_t (*callback)(int,int), Images &partial_images) { ColorVal min,max; int nump = images[0].numPlanes(); int progressive_qual_target = 0; if (callback) { // initialize planes to grey for (int p=0; p<nump; p++) { for (int fr=0; fr< (int)images.size(); fr++) { for (uint32_t r=0; r<images[fr].rows(); r++) { for (uint32_t c=0; c<images[fr].cols(); c++) { images[fr].set(p,r,c,(ranges->min(p)+ranges->max(p))/2); } } } } } for (int k=0,i=0; k < 5; k++) { int p=PLANE_ORDERING[k]; if (p>=nump) continue; i++; Properties properties((nump>3?NB_PROPERTIES_scanlinesA[p]:NB_PROPERTIES_scanlines[p])); v_printf(2,"\r%i%% done [%i/%i] DEC[%ux%u] ",(int)(100*pixels_done/pixels_todo),i,nump,images[0].cols(),images[0].rows()); v_printf(4,"\n"); pixels_done += images[0].cols()*images[0].rows(); if (ranges->min(p) < ranges->max(p)) for (uint32_t r = 0; r < images[0].rows(); r++) { for (int fr=0; fr< (int)images.size(); fr++) { Image& image = images[fr]; uint32_t begin=image.col_begin[r], end=image.col_end[r]; if (image.seen_before >= 0) { for(uint32_t c=0; c<image.cols(); c++) image.set(p,r,c,images[image.seen_before](p,r,c)); continue; } if (fr>0) { for (uint32_t c = 0; c < begin; c++) if (nump>3 && p<3 && image(3,r,c) == 0) image.set(p,r,c,predict(image,p,r,c)); else if (p !=4 ) image.set(p,r,c,images[fr-1](p,r,c)); /* else if (nump>4 && p<4 && image(4,r,c) > 0) image.set(p,r,c,images[fr-image(4,r,c)](p,r,c)); else { int oldframe=fr-1; image.set(p,r,c,images[oldframe](p,r,c)); while(p == 4 && image(p,r,c) > 0) {oldframe -= image(p,r,c); assert(oldframe>=0); image.set(p,r,c,images[oldframe](p,r,c));} } */ } else { if (nump>3 && p<3) { begin=0; end=image.cols(); } } for (uint32_t c = begin; c < end; c++) { if (nump>3 && p<3 && image(3,r,c) == 0) {image.set(p,r,c,predict(image,p,r,c)); continue;} if (nump>4 && p<4 && image(4,r,c) > 0) {assert(fr >= image(4,r,c)); image.set(p,r,c,images[fr-image(4,r,c)](p,r,c)); continue;} ColorVal guess = predict_and_calcProps_scanlines(properties,ranges,image,p,r,c,min,max); if (p==4 && max > fr) max = fr; ColorVal curr = coders[p].read_int(properties, min - guess, max - guess) + guess; image.set(p,r,c, curr); } if (fr>0) { for (uint32_t c = end; c < image.cols(); c++) if (nump>3 && p<3 && image(3,r,c) == 0) image.set(p,r,c,predict(image,p,r,c)); else if (p !=4 ) image.set(p,r,c,images[fr-1](p,r,c)); /* else if (nump>4 && p<4 && image(4,r,c) > 0) image.set(p,r,c,images[fr-image(4,r,c)](p,r,c)); else { int oldframe=fr-1; image.set(p,r,c,images[oldframe](p,r,c)); while(p == 4 && image(p,r,c) > 0) {oldframe -= image(p,r,c); assert(oldframe>=0); image.set(p,r,c,images[oldframe](p,r,c));} }*/ } } } int qual = 10000*pixels_done/pixels_todo; if (callback && qual >= progressive_qual_target) { for (unsigned int n=0; n < images.size(); n++) partial_images[n] = images[n].clone(); // make a copy to work with for (int i=transforms.size()-1; i>=0; i--) if (transforms[i]->undo_redo_during_decode()) transforms[i]->invData(partial_images); progressive_qual_target = callback(qual,rac.ftell()); if (qual >= progressive_qual_target) break; } } }
template<typename IO, typename Rac, typename Coder> void flif_decode_FLIF2_inner(Rac &rac, std::vector<Coder> &coders, Images &images, const ColorRanges *ranges, const int beginZL, const int endZL, int quality, int scale, std::vector<Transform<IO>*> &transforms, uint32_t (*callback)(int,int), Images &partial_images) { ColorVal min,max; int nump = images[0].numPlanes(); int progressive_qual_target = 0; // if (quality >= 0) { // quality = plane_zoomlevels(image, beginZL, endZL) * quality / 100; // } // flif_decode for (int i = 0; i < plane_zoomlevels(images[0], beginZL, endZL); i++) { std::pair<int, int> pzl = plane_zoomlevel(images[0], beginZL, endZL, i); int p = pzl.first; int z = pzl.second; if ((100*pixels_done > quality*pixels_todo) || 1<<(z/2) < scale) { flif_decode_FLIF2_inner_interpol(images, ranges, i, beginZL, endZL, (z%2 == 0 ?1:0), scale); return; } if (endZL == 0) v_printf(2,"\r%i%% done [%i/%i] DEC[%i,%ux%u] ",(int)(100*pixels_done/pixels_todo),i,plane_zoomlevels(images[0], beginZL, endZL)-1,p,images[0].cols(z),images[0].rows(z)); pixels_done += (images[0].cols(z)/(z%2==0?1:2))*(images[0].rows(z)/(z%2==0?2:1)); if (ranges->min(p) < ranges->max(p)) { ColorVal curr; Properties properties((nump>3?NB_PROPERTIESA[p]:NB_PROPERTIES[p])); if (z % 2 == 0) { for (uint32_t r = 1; r < images[0].rows(z); r += 2) { #ifdef CHECK_FOR_BROKENFILES if (rac.isEOF()) { v_printf(1,"Row %i: Unexpected file end. Interpolation from now on.\n",r); flif_decode_FLIF2_inner_interpol(images, ranges, i, beginZL, endZL, (r>1?r-2:r), scale); return; } #endif for (int fr=0; fr<(int)images.size(); fr++) { Image& image = images[fr]; if (image.seen_before >= 0) { for (uint32_t c=0; c<image.cols(z); c++) image.set(p,z,r,c,images[image.seen_before](p,z,r,c)); continue; } uint32_t begin=image.col_begin[r*image.zoom_rowpixelsize(z)]/image.zoom_colpixelsize(z), end=1+(image.col_end[r*image.zoom_rowpixelsize(z)]-1)/image.zoom_colpixelsize(z); if (fr>0) { for (uint32_t c = 0; c < begin; c++) if (nump>3 && p<3 && image(3,z,r,c) == 0) image.set(p,z,r,c, predict(image,z,p,r,c)); else if (p !=4 ) image.set(p,z,r,c,images[fr-1](p,z,r,c)); /* else if (nump>4 && p<4 && image(4,z,r,c) > 0) image.set(p,z,r,c,images[fr-image(4,z,r,c)](p,z,r,c)); else { int oldframe=fr-1; image.set(p,z,r,c,images[oldframe](p,z,r,c)); while(p == 4 && image(p,z,r,c) > 0) {oldframe -= image(p,z,r,c); assert(oldframe>=0); image.set(p,z,r,c,images[oldframe](p,z,r,c));}} */ for (uint32_t c = end; c < image.cols(z); c++) if (nump>3 && p<3 && image(3,z,r,c) == 0) image.set(p,z,r,c, predict(image,z,p,r,c)); else if (p !=4 ) image.set(p,z,r,c,images[fr-1](p,z,r,c)); /* else if (nump>4 && p<4 && image(4,z,r,c) > 0) image.set(p,z,r,c,images[fr-image(4,z,r,c)](p,z,r,c)); else { int oldframe=fr-1; image.set(p,z,r,c,images[oldframe](p,z,r,c)); while(p == 4 && image(p,z,r,c) > 0) {oldframe -= image(p,z,r,c); assert(oldframe>=0); image.set(p,z,r,c,images[oldframe](p,z,r,c));}} */ } else { if (nump>3 && p<3) {begin=0; end=image.cols(z);} } for (uint32_t c = begin; c < end; c++) { if (nump>3 && p<3 && image(3,z,r,c) == 0) { image.set(p,z,r,c,predict(image,z,p,r,c)); continue;} if (nump>4 && p<4 && image(4,z,r,c) > 0) { image.set(p,z,r,c,images[fr-image(4,z,r,c)](p,z,r,c)); continue;} ColorVal guess = predict_and_calcProps(properties,ranges,image,z,p,r,c,min,max); if (p==4 && max > fr) max = fr; curr = coders[p].read_int(properties, min - guess, max - guess) + guess; image.set(p,z,r,c, curr); } } } } else { for (uint32_t r = 0; r < images[0].rows(z); r++) { #ifdef CHECK_FOR_BROKENFILES if (rac.isEOF()) { v_printf(1,"Row %i: Unexpected file end. Interpolation from now on.\n", r); flif_decode_FLIF2_inner_interpol(images, ranges, i, beginZL, endZL, (r>0?r-1:r), scale); return; } #endif for (int fr=0; fr<(int)images.size(); fr++) { Image& image = images[fr]; if (image.seen_before >= 0) { for (uint32_t c=1; c<image.cols(z); c+=2) image.set(p,z,r,c,images[image.seen_before](p,z,r,c)); continue; } uint32_t begin=(image.col_begin[r*image.zoom_rowpixelsize(z)]/image.zoom_colpixelsize(z)), end=(1+(image.col_end[r*image.zoom_rowpixelsize(z)]-1)/image.zoom_colpixelsize(z))|1; if (begin>1 && ((begin&1) ==0)) begin--; if (begin==0) begin=1; if (fr>0) { for (uint32_t c = 1; c < begin; c+=2) if (nump>3 && p<3 && image(3,z,r,c) == 0) image.set(p,z,r,c, predict(image,z,p,r,c)); else if (p !=4 ) image.set(p,z,r,c,images[fr-1](p,z,r,c)); /* else if (nump>4 && p<4 && image(4,z,r,c) > 0) image.set(p,z,r,c,images[fr-image(4,z,r,c)](p,z,r,c)); else { int oldframe=fr-1; image.set(p,z,r,c,images[oldframe](p,z,r,c)); while(p == 4 && image(p,z,r,c) > 0) {oldframe -= image(p,z,r,c); assert(oldframe>=0); image.set(p,z,r,c,images[oldframe](p,z,r,c));}} */ for (uint32_t c = end; c < image.cols(z); c+=2) if (nump>3 && p<3 && image(3,z,r,c) == 0) image.set(p,z,r,c, predict(image,z,p,r,c)); else if (p !=4 ) image.set(p,z,r,c,images[fr-1](p,z,r,c)); /* else if (nump>4 && p<4 && image(4,z,r,c) > 0) image.set(p,z,r,c,images[fr-image(4,z,r,c)](p,z,r,c)); else { int oldframe=fr-1; image.set(p,z,r,c,images[oldframe](p,z,r,c)); while(p == 4 && image(p,z,r,c) > 0) {oldframe -= image(p,z,r,c); assert(oldframe>=0); image.set(p,z,r,c,images[oldframe](p,z,r,c));}} */ } else { if (nump>3 && p<3) {begin=1; end=image.cols(z);} } for (uint32_t c = begin; c < end; c+=2) { if (nump>3 && p<3 && image(3,z,r,c) == 0) { image.set(p,z,r,c,predict(image,z,p,r,c)); continue;} if (nump>4 && p<4 && image(4,z,r,c) > 0) { image.set(p,z,r,c,images[fr-image(4,z,r,c)](p,z,r,c)); continue;} ColorVal guess = predict_and_calcProps(properties,ranges,image,z,p,r,c,min,max); if (p==4 && max > fr) max = fr; curr = coders[p].read_int(properties, min - guess, max - guess) + guess; image.set(p,z,r,c, curr); } } } } if (endZL==0) { v_printf(3," read %li bytes ", rac.ftell()); v_printf(5,"\n"); } } int qual = 10000*pixels_done/pixels_todo; if (callback && (endZL==0 || i+1 == plane_zoomlevels(images[0], beginZL, endZL)) && qual >= progressive_qual_target) { for (unsigned int n=0; n < images.size(); n++) partial_images[n] = images[n].clone(); // make a copy to work with int64_t pixels_really_done = pixels_done; flif_decode_FLIF2_inner_interpol(partial_images, ranges, i+1, beginZL, endZL, -1, scale); if (endZL>0) flif_decode_FLIF2_inner_interpol(partial_images, ranges, 0, endZL-1, 0, -1, scale); pixels_done = pixels_really_done; for (int i=transforms.size()-1; i>=0; i--) if (transforms[i]->undo_redo_during_decode()) transforms[i]->invData(partial_images); progressive_qual_target = callback(qual,rac.ftell()); if (qual >= progressive_qual_target) break; } } }
int handle_decode(int argc, char **argv, Images &images, flif_options &options) { if (options.scale < 0) { // just identify the file(s), don't actually decode while (argc>0) { decode_flif(argv, images, options); argv++; argc--; } return 0; } if (argc == 1 && options.show_breakpoints) { decode_flif(argv, images, options); return 0; } char *ext = strrchr(argv[1],'.'); if (check_metadata_extension(ext)) { // only requesting metadata, no need to actually decode the file options.scale = -2; decode_flif(argv, images, options); if (!images[0].save(argv[1])) return 2; v_printf(2,"\n"); return 0; } if (!check_compatible_extension(ext) && strcmp(argv[1],"null:") && strcmp(argv[1],"-")) { e_printf("Error: expected \".png\", \".pnm\" or \".pam\" file name extension for output file\n"); return 1; } if (!(ext && ( !strcasecmp(ext,".png")))) options.keep_palette = false; // don't try to make a palette PNM try { if (!decode_flif(argv, images, options)) { e_printf("Error: could not decode FLIF file\n"); return 3; } } catch (std::bad_alloc& ba) { e_printf("Error: memory allocation problem (unexpected)\n"); return 3; } if (!strcmp(argv[1],"null:")) return 0; // if (scale>1) // v_printf(3,"Downscaling output: %ux%u -> %ux%u\n",images[0].cols(),images[0].rows(),images[0].cols()/scale,images[0].rows()/scale); if (images.size() == 1) { if (!images[0].save(argv[1])) return 2; } else { bool to_stdout=false; if (!strcmp(argv[1],"-")) { to_stdout=true; v_printf(1,"Warning: writing animation to standard output as a concatenation of PAM files.\n"); } int counter=0; int maxlength = strlen(argv[1])+100; std::vector<char> vfilename(maxlength); char *filename = &vfilename[0]; bool use_custom_format = false; if (strchr(argv[1],'%')) use_custom_format = true; strcpy(filename,argv[1]); char *a_ext = strrchr(filename,'.'); if (!a_ext && !to_stdout) { e_printf("Problem saving animation to %s\n",filename); return 2; } for (Image& image : images) { if (!to_stdout) { if (use_custom_format) snprintf(filename,maxlength,argv[1],counter); else if (images.size() < 1000) sprintf(a_ext,"-%03d%s",counter,ext); else if (images.size() < 10000) sprintf(a_ext,"-%04d%s",counter,ext); else if (images.size() < 100000) sprintf(a_ext,"-%05d%s",counter,ext); else sprintf(a_ext,"-%08d%s",counter,ext); if (file_exists(filename) && !options.overwrite) { e_printf("Error: output file already exists: %s\nUse --overwrite to force overwrite.\n",filename); return 4; } if (!image.save(filename)) return 2; } else { if (!image.save(argv[1])) return 2; } counter++; v_printf(2," (%i/%i) \r",counter,(int)images.size()); v_printf(4,"\n"); } } // get rid of palette (should also do this in the non-standard/error paths, but being lazy here since the tool will exit anyway) images[0].clear(); v_printf(2,"\n"); return 0; }
bool encode_flif(int argc, char **argv, Images &images, flif_options &options) { bool flat=true; unsigned int framenb=0; for (Image& i : images) { i.frame_delay = options.frame_delay[framenb]; if (framenb+1 < options.frame_delay.size()) framenb++; } for (Image &image : images) if (image.uses_alpha()) flat=false; if (flat && images[0].numPlanes() == 4) { v_printf(2,"Alpha channel not actually used, dropping it.\n"); for (Image &image : images) image.drop_alpha(); } bool grayscale=true; for (Image &image : images) if (image.uses_color()) grayscale=false; if (grayscale && images[0].numPlanes() == 3) { v_printf(2,"Chroma not actually used, dropping it.\n"); for (Image &image : images) image.drop_color(); } uint64_t nb_pixels = (uint64_t)images[0].rows() * images[0].cols(); std::vector<std::string> desc; if (nb_pixels > 2) { // no point in doing anything for 1- or 2-pixel images if (options.plc && (images[0].getDepth() > 8 || !options.loss)) { desc.push_back("Channel_Compact"); // compactify channels (not if lossy, because then loss gets magnified!) } if (options.ycocg) { desc.push_back("YCoCg"); // convert RGB(A) to YCoCg(A) } desc.push_back("PermutePlanes"); // permute RGB to GRB desc.push_back("Bounds"); // get the bounds of the color spaces } // only use palette/CB if we're lossless, because lossy and palette don't go well together... if (options.palette_size == -1) { options.palette_size = DEFAULT_MAX_PALETTE_SIZE; if (nb_pixels * images.size() / 3 < DEFAULT_MAX_PALETTE_SIZE) { options.palette_size = nb_pixels * images.size() / 3; } } if (!options.loss && options.palette_size != 0) { desc.push_back("Palette_Alpha"); // try palette (including alpha) desc.push_back("Palette"); // try palette (without alpha) } if (!options.loss) { if (options.acb == -1) { // not specified if ACB should be used if (nb_pixels * images.size() > 10000) { desc.push_back("Color_Buckets"); // try auto color buckets on large images } } else if (options.acb) { desc.push_back("Color_Buckets"); // try auto color buckets if forced } } if (options.method.o == Optional::undefined) { // no method specified, pick one heuristically if (nb_pixels * images.size() < 10000) options.method.encoding=flifEncoding::nonInterlaced; // if the image is small, not much point in doing interlacing else options.method.encoding=flifEncoding::interlaced; // default method: interlacing } if (images.size() > 1) { desc.push_back("Duplicate_Frame"); // find duplicate frames if (!options.loss) { // only if lossless if (options.frs) desc.push_back("Frame_Shape"); // get the shapes of the frames if (options.lookback) desc.push_back("Frame_Lookback"); // make a "deep" alpha channel (negative values are transparent to some previous frame) } } if (options.learn_repeats < 0) { // no number of repeats specified, pick a number heuristically options.learn_repeats = TREE_LEARN_REPEATS; //if (nb_pixels * images.size() < 5000) learn_repeats--; // avoid large trees for small images if (options.learn_repeats < 0) options.learn_repeats=0; } bool result = true; if (!options.just_add_loss) { FILE *file = NULL; if (!strcmp(argv[0],"-")) file = stdout; else file = fopen(argv[0],"wb"); if (!file) return false; FileIO fio(file, (file == stdout? "to standard output" : argv[0])); if (!flif_encode(fio, images, desc, options)) result = false; } else { BlobIO bio; // will just contain some unneeded FLIF header stuff if (!flif_encode(bio, images, desc, options)) result = false; else if (!images[0].save(argv[0])) result = false; } // get rid of palette images[0].clear(); return result; }
bool encode_load_input_images(int argc, char **argv, Images &images, flif_options &options) { int nb_input_images = argc-1; int nb_actual_images = 0; metadata_options md; md.icc = options.color_profile; md.xmp = options.metadata; md.exif = options.metadata; while(argc>1) { int maxlength = strlen(argv[0])+100; std::vector<char> vfilename(maxlength); char *filename = argv[0]; int framecounter = 0; int stop_searching = 0; bool multiple=false; if (!file_exists(argv[0]) && strchr(argv[0],'%')) { multiple=true; filename = &vfilename[0]; } for ( ; framecounter < 0xFFFFFFF && stop_searching < 1000; framecounter++ ) { if (multiple) { snprintf(filename,maxlength,argv[0],framecounter); if (!file_exists(filename)) { stop_searching++; continue; } stop_searching = 0; } Image image; if (options.keep_palette) image.palette = true; // tells the PNG loader to keep palette intact v_printf_tty(2,"\r"); if (!image.load(filename,md)) { e_printf("Could not read input file: %s\n", argv[0]); return false; }; if (image.numPlanes() > 0) { nb_actual_images++; images.push_back(std::move(image)); Image& last_image = images.back(); if (last_image.rows() != images[0].rows() || last_image.cols() != images[0].cols()) { e_printf("Dimensions of all input images should be the same!\n"); e_printf(" First image is %ux%u\n",images[0].cols(),images[0].rows()); e_printf(" This image is %ux%u: %s\n",last_image.cols(),last_image.rows(),filename); return false; } if (last_image.numPlanes() < images[0].numPlanes()) { if (images[0].numPlanes() == 3) last_image.ensure_chroma(); else if (images[0].numPlanes() == 4) last_image.ensure_alpha(); else { e_printf("Problem while loading input images, please report this.\n"); return false; } } else if (last_image.numPlanes() > images[0].numPlanes()) { if (last_image.numPlanes() == 3) { for (Image& i : images) i.ensure_chroma(); } else if (last_image.numPlanes() == 4) { for (Image& i : images) i.ensure_alpha(); } else { e_printf("Problem while loading input images, please report this.\n"); return false; } } } else { if (images.size() == 0) { e_printf("First specify the actual image(s), then the metadata.\n"); return false; } for (size_t i=0; i<image.metadata.size(); i++) images[0].metadata.push_back(image.metadata[i]); } if (nb_input_images>1) {v_printf(2," (%i/%i) ",(int)images.size(),nb_input_images); v_printf(4,"\n");} if (!multiple) break; } argc--; argv++; } if (nb_actual_images > 0) return true; e_printf("Error: no actual input images to be encoded!\n"); return false; }
template<typename Rac, typename Coder> void flif_decode_FLIF2_inner(Rac &rac, std::vector<Coder> &coders, Images &images, const ColorRanges *ranges, const int beginZL, const int endZL, int quality, int scale) { ColorVal min,max; int nump = images[0].numPlanes(); // if (quality >= 0) { // quality = plane_zoomlevels(image, beginZL, endZL) * quality / 100; // } // flif_decode for (int i = 0; i < plane_zoomlevels(images[0], beginZL, endZL); i++) { std::pair<int, int> pzl = plane_zoomlevel(images[0], beginZL, endZL, i); int p = pzl.first; int z = pzl.second; if ((100*pixels_done > quality*pixels_todo) || 1<<(z/2) < scale) { flif_decode_FLIF2_inner_interpol(images, ranges, i, beginZL, endZL, (z%2 == 0 ?1:0), scale); return; } if (endZL == 0) v_printf(2,"\r%i%% done [%i/%i] DEC[%i,%ux%u] ",(int)(100*pixels_done/pixels_todo),i,plane_zoomlevels(images[0], beginZL, endZL)-1,p,images[0].cols(z),images[0].rows(z)); pixels_done += images[0].cols(z)*images[0].rows(z)/2; if (ranges->min(p) >= ranges->max(p)) continue; ColorVal curr; Properties properties((nump>3?NB_PROPERTIESA[p]:NB_PROPERTIES[p])); if (z % 2 == 0) { for (uint32_t r = 1; r < images[0].rows(z); r += 2) { #ifdef CHECK_FOR_BROKENFILES if (rac.isEOF()) { v_printf(1,"Row %i: Unexpected file end. Interpolation from now on.\n",r); flif_decode_FLIF2_inner_interpol(images, ranges, i, beginZL, endZL, (r>1?r-2:r), scale); return; } #endif for (int fr=0; fr<(int)images.size(); fr++) { Image& image = images[fr]; if (image.seen_before >= 0) { for (uint32_t c=0; c<image.cols(z); c++) image.set(p,z,r,c,images[image.seen_before](p,z,r,c)); continue; } uint32_t begin=image.col_begin[r*image.zoom_rowpixelsize(z)]/image.zoom_colpixelsize(z), end=1+(image.col_end[r*image.zoom_rowpixelsize(z)]-1)/image.zoom_colpixelsize(z); if (fr>0) { for (uint32_t c = 0; c < begin; c++) if (nump>3 && p<3 && image(3,z,r,c) == 0) image.set(p,z,r,c, predict(image,z,p,r,c)); else if (p !=4 ) image.set(p,z,r,c,images[fr-1](p,z,r,c)); /* else if (nump>4 && p<4 && image(4,z,r,c) > 0) image.set(p,z,r,c,images[fr-image(4,z,r,c)](p,z,r,c)); else { int oldframe=fr-1; image.set(p,z,r,c,images[oldframe](p,z,r,c)); while(p == 4 && image(p,z,r,c) > 0) {oldframe -= image(p,z,r,c); assert(oldframe>=0); image.set(p,z,r,c,images[oldframe](p,z,r,c));}} */ for (uint32_t c = end; c < image.cols(z); c++) if (nump>3 && p<3 && image(3,z,r,c) == 0) image.set(p,z,r,c, predict(image,z,p,r,c)); else if (p !=4 ) image.set(p,z,r,c,images[fr-1](p,z,r,c)); /* else if (nump>4 && p<4 && image(4,z,r,c) > 0) image.set(p,z,r,c,images[fr-image(4,z,r,c)](p,z,r,c)); else { int oldframe=fr-1; image.set(p,z,r,c,images[oldframe](p,z,r,c)); while(p == 4 && image(p,z,r,c) > 0) {oldframe -= image(p,z,r,c); assert(oldframe>=0); image.set(p,z,r,c,images[oldframe](p,z,r,c));}} */ } else { if (nump>3 && p<3) {begin=0; end=image.cols(z);} } for (uint32_t c = begin; c < end; c++) { if (nump>3 && p<3 && image(3,z,r,c) == 0) { image.set(p,z,r,c,predict(image,z,p,r,c)); continue;} if (nump>4 && p<4 && image(4,z,r,c) > 0) { image.set(p,z,r,c,images[fr-image(4,z,r,c)](p,z,r,c)); continue;} ColorVal guess = predict_and_calcProps(properties,ranges,image,z,p,r,c,min,max); if (p==4 && max > fr) max = fr; curr = coders[p].read_int(properties, min - guess, max - guess) + guess; image.set(p,z,r,c, curr); } } } } else { for (uint32_t r = 0; r < images[0].rows(z); r++) { #ifdef CHECK_FOR_BROKENFILES if (rac.isEOF()) { v_printf(1,"Row %i: Unexpected file end. Interpolation from now on.\n", r); flif_decode_FLIF2_inner_interpol(images, ranges, i, beginZL, endZL, (r>0?r-1:r), scale); return; } #endif for (int fr=0; fr<(int)images.size(); fr++) { Image& image = images[fr]; if (image.seen_before >= 0) { for (uint32_t c=1; c<image.cols(z); c+=2) image.set(p,z,r,c,images[image.seen_before](p,z,r,c)); continue; } uint32_t begin=(image.col_begin[r*image.zoom_rowpixelsize(z)]/image.zoom_colpixelsize(z)), end=(1+(image.col_end[r*image.zoom_rowpixelsize(z)]-1)/image.zoom_colpixelsize(z))|1; if (begin>1 && ((begin&1) ==0)) begin--; if (begin==0) begin=1; if (fr>0) { for (uint32_t c = 1; c < begin; c+=2) if (nump>3 && p<3 && image(3,z,r,c) == 0) image.set(p,z,r,c, predict(image,z,p,r,c)); else if (p !=4 ) image.set(p,z,r,c,images[fr-1](p,z,r,c)); /* else if (nump>4 && p<4 && image(4,z,r,c) > 0) image.set(p,z,r,c,images[fr-image(4,z,r,c)](p,z,r,c)); else { int oldframe=fr-1; image.set(p,z,r,c,images[oldframe](p,z,r,c)); while(p == 4 && image(p,z,r,c) > 0) {oldframe -= image(p,z,r,c); assert(oldframe>=0); image.set(p,z,r,c,images[oldframe](p,z,r,c));}} */ for (uint32_t c = end; c < image.cols(z); c+=2) if (nump>3 && p<3 && image(3,z,r,c) == 0) image.set(p,z,r,c, predict(image,z,p,r,c)); else if (p !=4 ) image.set(p,z,r,c,images[fr-1](p,z,r,c)); /* else if (nump>4 && p<4 && image(4,z,r,c) > 0) image.set(p,z,r,c,images[fr-image(4,z,r,c)](p,z,r,c)); else { int oldframe=fr-1; image.set(p,z,r,c,images[oldframe](p,z,r,c)); while(p == 4 && image(p,z,r,c) > 0) {oldframe -= image(p,z,r,c); assert(oldframe>=0); image.set(p,z,r,c,images[oldframe](p,z,r,c));}} */ } else { if (nump>3 && p<3) {begin=1; end=image.cols(z);} } for (uint32_t c = begin; c < end; c+=2) { if (nump>3 && p<3 && image(3,z,r,c) == 0) { image.set(p,z,r,c,predict(image,z,p,r,c)); continue;} if (nump>4 && p<4 && image(4,z,r,c) > 0) { image.set(p,z,r,c,images[fr-image(4,z,r,c)](p,z,r,c)); continue;} ColorVal guess = predict_and_calcProps(properties,ranges,image,z,p,r,c,min,max); if (p==4 && max > fr) max = fr; curr = coders[p].read_int(properties, min - guess, max - guess) + guess; image.set(p,z,r,c, curr); } } } } if (endZL==0) { v_printf(3," read %li bytes ", rac.ftell()); v_printf(5,"\n"); } } }
bool encode_flif(int argc, char **argv, Images &images, int palette_size, int acb, flifEncodingOptional method, int lookback, int learn_repeats, std::vector<int> &frame_delay, int divisor=CONTEXT_TREE_COUNT_DIV, int min_size=CONTEXT_TREE_MIN_SUBTREE_SIZE, int split_threshold=CONTEXT_TREE_SPLIT_THRESHOLD, int yiq=1, int plc=1, int frs=1, int cutoff=2, int alpha=19, int crc_check=-1, int loss=0) { bool flat=true; unsigned int framenb=0; for (Image& i : images) { i.frame_delay = frame_delay[framenb]; if (framenb+1 < frame_delay.size()) framenb++; } for (Image &image : images) if (image.uses_alpha()) flat=false; if (flat && images[0].numPlanes() == 4) { v_printf(2,"Alpha channel not actually used, dropping it.\n"); for (Image &image : images) image.drop_alpha(); } bool grayscale=true; for (Image &image : images) if (image.uses_color()) grayscale=false; if (grayscale && images[0].numPlanes() == 3) { v_printf(2,"Chroma not actually used, dropping it.\n"); for (Image &image : images) image.drop_color(); } uint64_t nb_pixels = (uint64_t)images[0].rows() * images[0].cols(); std::vector<std::string> desc; if (nb_pixels > 2) { // no point in doing anything for 1- or 2-pixel images if (plc && !loss) { desc.push_back("Channel_Compact"); // compactify channels (not if lossy, because then loss gets magnified!) } if (yiq) { desc.push_back("YCoCg"); // convert RGB(A) to YCoCg(A) } desc.push_back("Bounds"); // get the bounds of the color spaces } if (!loss) { // only use palette/CB if we're lossless, because lossy and palette don't go well together... if (palette_size == -1) { palette_size = 1024; if (nb_pixels * images.size() / 2 < 1024) { palette_size = nb_pixels * images.size() / 2; } } if (palette_size != 0) { desc.push_back("Palette_Alpha"); // try palette (including alpha) } if (palette_size != 0) { desc.push_back("Palette"); // try palette (without alpha) } if (acb == -1) { // not specified if ACB should be used if (nb_pixels * images.size() > 10000) { desc.push_back("Color_Buckets"); // try auto color buckets on large images } } else if (acb) { desc.push_back("Color_Buckets"); // try auto color buckets if forced } } if (method.o == Optional::undefined) { // no method specified, pick one heuristically if (nb_pixels * images.size() < 10000) method.encoding=flifEncoding::nonInterlaced; // if the image is small, not much point in doing interlacing else method.encoding=flifEncoding::interlaced; // default method: interlacing } if (images.size() > 1) { desc.push_back("Duplicate_Frame"); // find duplicate frames if (!loss) { // only if lossless if (frs) desc.push_back("Frame_Shape"); // get the shapes of the frames if (lookback) desc.push_back("Frame_Lookback"); // make a "deep" alpha channel (negative values are transparent to some previous frame) } } if (learn_repeats < 0) { // no number of repeats specified, pick a number heuristically learn_repeats = TREE_LEARN_REPEATS; if (nb_pixels * images.size() < 5000) learn_repeats--; // avoid large trees for small images if (learn_repeats < 0) learn_repeats=0; } FILE *file = fopen(argv[0],"wb"); if (!file) return false; FileIO fio(file, argv[0]); return flif_encode(fio, images, desc, method.encoding, learn_repeats, acb, palette_size, lookback, divisor, min_size, split_threshold, cutoff, alpha, crc_check, loss); }
void Pipeline::run(const bool save_clouds, const bool show_clouds) { Logger _log("Pipeline"); /** * Stage 0: Load images from file */ Images images; load_images(folder_path, images); /** * Stage 1: Detect features in loaded images */ CamFrames cam_Frames; DescriptorsVec descriptors_vec; extract_features(images, cam_Frames, descriptors_vec); // Free Image.gray for (int i = 0; i < images.size(); i++) const_cast<cv::Mat&>(images[i].gray).release(); /** * Stage 2: Calculate descriptors and find image pairs through matching */ ImagePairs image_pairs; find_matching_pairs(images, cam_Frames, descriptors_vec, image_pairs); // Free some memory DescriptorsVec().swap(descriptors_vec); /** * State 3: Compute pairwise R and t */ register_camera(image_pairs, cam_Frames); /** * Stage 4: Construct associativity matrix and spanning tree */ Associativity assocMat(cam_Frames.size()); for (int p = 0; p < image_pairs.size(); p++) { ImagePair* pair = &image_pairs[p]; int i = pair->pair_index.first, j = pair->pair_index.second; if (pair -> R.empty()) continue; assocMat(i, j) = pair; assocMat(j, i) = pair; assert(assocMat(i, j) == assocMat(j, i)); assert(assocMat(i, j)->pair_index.first == i && assocMat(i, j)->pair_index.second == j); } Associativity tree; const int camera_num = build_spanning_tree(image_pairs, assocMat, tree); /** * Stage 5: Compute global Rs and ts */ CameraPoses gCameraPoses; glo_cam_poses(images, gCameraPoses, image_pairs, tree); /** * Stage 6: Find and cluster depth points from local camera frame to global camera frame */ PointClusters pointClusters; PointMap pointMap; find_clusters(assocMat, gCameraPoses, cam_Frames, pointClusters, pointMap); // Free some memory ImagePairs().swap(image_pairs); /** * Stage 7: get center of mass from clusters */ PointCloud pointCloud(pointClusters.size()); find_CoM(pointClusters, pointCloud); // Free pointClusters for (int i = 0; i < pointClusters.size(); i++) PointCluster().swap(pointClusters[i]); PointClusters().swap(pointClusters); // Save cloud before BA Viewer viewer("Before BA"); auto cloud = viewer.createPointCloud(images, gCameraPoses, cameraMatrix); int n = cloud->points.size(); auto time = ptime::second_clock::local_time(); auto tstamp = ptime::to_iso_string(time); auto folder = fs::path(folder_path).filename().string(); auto fname = (fmt("%s_%s_%d_noBA.pcd") % folder % tstamp % n).str(); if (save_clouds) viewer.saveCloud(cloud, fname); if (show_clouds) viewer.showCloudPoints(cloud, false); /** * State 8: Bundle Adjustment */ bundle_adjustment(pointMap, cam_Frames, false, gCameraPoses, pointCloud); _log.tok(); /** * Show calculated point cloud */ Viewer viewer_ba("After BA no Depth"); cloud = viewer_ba.createPointCloud(images, gCameraPoses, cameraMatrix); n = cloud->points.size(); fname = (fmt("%s_%s_%d_BA_noD.pcd") % folder % tstamp % n).str(); if (save_clouds) viewer_ba.saveCloud(cloud, fname); if (show_clouds) viewer_ba.showCloudPoints(cloud,false); bundle_adjustment(pointMap, cam_Frames, true, gCameraPoses, pointCloud); // Free some memory PointMap().swap(pointMap); CamFrames().swap(cam_Frames); PointCloud().swap(pointCloud); /** * Show calculated point cloud */ Viewer viewer_baD("After BA with Depth"); cloud = viewer_baD.createPointCloud(images, gCameraPoses, cameraMatrix); n = cloud->points.size(); fname = (fmt("%s_%s_%d_BA_D.pcd") % folder % tstamp % n).str(); // Free some memory Images().swap(images); CameraPoses().swap(gCameraPoses); if (save_clouds) viewer_baD.saveCloud(cloud, fname); if (show_clouds) viewer_baD.showCloudPoints(cloud); }
int main(int argc, char **argv) { Images images; int mode = 0; // 0 = encode, 1 = decode int method = 0; // 1=non-interlacing, 2=interlacing int quality = 100; // 100 = everything, positive value: partial decode, negative value: only rough data int learn_repeats = -1; int acb = -1; // try auto color buckets int scale = 1; int frame_delay = 100; int palette_size = 512; int lookback = 1; if (strcmp(argv[0],"flif") == 0) mode = 0; if (strcmp(argv[0],"dflif") == 0) mode = 1; if (strcmp(argv[0],"deflif") == 0) mode = 1; if (strcmp(argv[0],"decflif") == 0) mode = 1; static struct option optlist[] = { {"help", 0, NULL, 'h'}, {"encode", 0, NULL, 'e'}, {"decode", 0, NULL, 'd'}, {"first", 1, NULL, 'f'}, {"verbose", 0, NULL, 'v'}, {"interlace", 0, NULL, 'i'}, {"no-interlace", 0, NULL, 'n'}, {"acb", 0, NULL, 'a'}, {"no-acb", 0, NULL, 'b'}, {"quality", 1, NULL, 'q'}, {"scale", 1, NULL, 's'}, {"palette", 1, NULL, 'p'}, {"repeats", 1, NULL, 'r'}, {"frame-delay", 1, NULL, 'f'}, {"lookback", 1, NULL, 'l'}, {0, 0, 0, 0} }; int i,c; while ((c = getopt_long (argc, argv, "hedvinabq:s:p:r:f:l:", optlist, &i)) != -1) { switch (c) { case 'e': mode=0; break; case 'd': mode=1; break; case 'v': verbosity++; break; case 'i': if (method==0) method=2; break; case 'n': method=1; break; case 'a': acb=1; break; case 'b': acb=0; break; case 'p': palette_size=atoi(optarg); if (palette_size < -1 || palette_size > 30000) {fprintf(stderr,"Not a sensible number for option -p\n"); return 1; } if (palette_size == 0) {v_printf(2,"Palette disabled\n"); } break; case 'q': quality=atoi(optarg); if (quality < -1 || quality > 100) {fprintf(stderr,"Not a sensible number for option -q\n"); return 1; } break; case 's': scale=atoi(optarg); if (scale < 1 || scale > 128) {fprintf(stderr,"Not a sensible number for option -s\n"); return 1; } break; case 'r': learn_repeats=atoi(optarg); if (learn_repeats < 0 || learn_repeats > 1000) {fprintf(stderr,"Not a sensible number for option -r\n"); return 1; } break; case 'f': frame_delay=atoi(optarg); if (frame_delay < 0 || frame_delay > 60000) {fprintf(stderr,"Not a sensible number for option -f\n"); return 1; } break; case 'l': lookback=atoi(optarg); if (lookback < -1 || lookback > 256) {fprintf(stderr,"Not a sensible number for option -l\n"); return 1; } break; case 'h': default: show_help(); return 0; } } argc -= optind; argv += optind; v_printf(3," _____ __ (__) _____"); v_printf(3,"\n (___ || | | || ___) ");v_printf(2,"FLIF 0.1 [2 October 2015]"); v_printf(3,"\n (__ || |_|__|| __) Free Lossless Image Format"); v_printf(3,"\n (__||______) |__) (c) 2010-2015 J.Sneyers & P.Wuille, GNU GPL v3+\n"); v_printf(3,"\n"); if (argc == 0) { //fprintf(stderr,"Input file missing.\n"); if (verbosity == 1) show_help(); return 1; } if (argc == 1) { fprintf(stderr,"Output file missing.\n"); show_help(); return 1; } if (file_exists(argv[0])) { if (mode == 0 && file_is_flif(argv[0])) { v_printf(2,"Input file is a FLIF file, adding implicit -d\n"); mode = 1; } char *f = strrchr(argv[0],'/'); char *ext = f ? strrchr(f,'.') : strrchr(argv[0],'.'); if (mode == 0) { if (ext && ( !strcasecmp(ext,".png") || !strcasecmp(ext,".pnm") || !strcasecmp(ext,".ppm") || !strcasecmp(ext,".pgm") || !strcasecmp(ext,".pbm") || !strcasecmp(ext,".pam"))) { // ok } else { fprintf(stderr,"Warning: expected \".png\" or \".pnm\" file name extension for input file, trying anyway...\n"); } } else { if (ext && ( !strcasecmp(ext,".flif") || ( !strcasecmp(ext,".flf") ))) { // ok } else { fprintf(stderr,"Warning: expected file name extension \".flif\" for input file, trying anyway...\n"); } } } else if (argc>0) { fprintf(stderr,"Input file does not exist: %s\n",argv[0]); return 1; } if (mode == 0) { int nb_input_images = argc-1; while(argc>1) { Image image; v_printf(2,"\r"); if (!image.load(argv[0])) { fprintf(stderr,"Could not read input file: %s\n", argv[0]); return 2; }; images.push_back(image); if (image.rows() != images[0].rows() || image.cols() != images[0].cols() || image.numPlanes() != images[0].numPlanes()) { fprintf(stderr,"Dimensions of all input images should be the same!\n"); fprintf(stderr," First image is %ux%u, %i channels.\n",images[0].cols(),images[0].rows(),images[0].numPlanes()); fprintf(stderr," This image is %ux%u, %i channels: %s\n",image.cols(),image.rows(),image.numPlanes(),argv[0]); return 2; } argc--; argv++; if (nb_input_images>1) {v_printf(2," (%i/%i) ",(int)images.size(),nb_input_images); v_printf(4,"\n");} } v_printf(2,"\n"); bool flat=true; for (Image &image : images) if (image.uses_alpha()) flat=false; if (flat && images[0].numPlanes() == 4) { v_printf(2,"Alpha channel not actually used, dropping it.\n"); for (Image &image : images) image.drop_alpha(); } uint64_t nb_pixels = (uint64_t)images[0].rows() * images[0].cols(); std::vector<std::string> desc; desc.push_back("YIQ"); // convert RGB(A) to YIQ(A) desc.push_back("BND"); // get the bounds of the color spaces if (palette_size > 0) desc.push_back("PLA"); // try palette (including alpha) if (palette_size > 0) desc.push_back("PLT"); // try palette (without alpha) if (acb == -1) { // not specified if ACB should be used if (nb_pixels > 10000) desc.push_back("ACB"); // try auto color buckets on large images } else if (acb) desc.push_back("ACB"); // try auto color buckets if forced if (method == 0) { // no method specified, pick one heuristically if (nb_pixels < 10000) method=1; // if the image is small, not much point in doing interlacing else method=2; // default method: interlacing } if (images.size() > 1) { desc.push_back("DUP"); // find duplicate frames desc.push_back("FRS"); // get the shapes of the frames if (lookback != 0) desc.push_back("FRA"); // make a "deep" alpha channel (negative values are transparent to some previous frame) } if (learn_repeats < 0) { // no number of repeats specified, pick a number heuristically learn_repeats = TREE_LEARN_REPEATS; if (nb_pixels < 5000) learn_repeats--; // avoid large trees for small images if (learn_repeats < 0) learn_repeats=0; } encode(argv[0], images, desc, method, learn_repeats, acb, frame_delay, palette_size, lookback); } else { char *ext = strrchr(argv[1],'.'); if (ext && ( !strcasecmp(ext,".png") || !strcasecmp(ext,".pnm") || !strcasecmp(ext,".ppm") || !strcasecmp(ext,".pgm") || !strcasecmp(ext,".pbm") || !strcasecmp(ext,".pam"))) { // ok } else { fprintf(stderr,"Error: expected \".png\", \".pnm\" or \".pam\" file name extension for output file\n"); return 1; } if (!decode(argv[0], images, quality, scale)) return 3; if (scale>1) v_printf(3,"Downscaling output: %ux%u -> %ux%u\n",images[0].cols(),images[0].rows(),images[0].cols()/scale,images[0].rows()/scale); if (images.size() == 1) { if (!images[0].save(argv[1],scale)) return 2; } else { int counter=0; std::vector<char> vfilename(strlen(argv[1])+6); char *filename = &vfilename[0]; strcpy(filename,argv[1]); char *a_ext = strrchr(filename,'.'); for (Image& image : images) { sprintf(a_ext,"-%03d%s",counter++,ext); if (!image.save(filename,scale)) return 2; v_printf(2," (%i/%i) \r",counter,(int)images.size()); v_printf(4,"\n"); } } v_printf(2,"\n"); } for (Image &image : images) image.clear(); return 0; }
void Pipeline::extract_features(const Images& images,CamFrames& cam_Frames,DescriptorsVec& descriptors_vec) { Logger _log("Step 1 (features)"); const int n = images.size(); cam_Frames = CamFrames(n); descriptors_vec = DescriptorsVec(n); // Create a SIFT detector // Bernhard-checked const int feature_num = 0; // Default: 0 const int octavelayers_num = 4; // Default: 3 const double constrast_thresh = .04f; // Default: 0.04 (larger: less feats) const double edge_threshold = 4.0f; // Default: 10 (larger: more feats) const double sigma = 1.6f; // Default: 1.6 Ptr<Feature2D> sift_detector = SIFT::create(feature_num, octavelayers_num, constrast_thresh, edge_threshold, sigma); // create brist detector // parameters for brisk : int thresh = 30, int octaves = 3, float patternScale = 1.0f // const int thresh = 70; // const int octaves = 2; // Ptr<Feature2D> brisk_detector = BRISK::create(thresh, octaves); // detect features in a loop #pragma omp parallel for for (int i = 0; i < n; ++i) { Image image = images[i]; KeyPoints key_points; Descriptors descriptors; // Detect keypoints and calculate descriptor vectors sift_detector->detectAndCompute(image.gray, noArray(), key_points, descriptors); KeyPoints keep_key_points; Descriptors keep_descriptors; Depths keep_depths; // Keep keypoints with valid depth only // Valid depth is [0.4, 8]m for (size_t k = 0; k < key_points.size(); k++) { float d = image.dep.at<float>(key_points[k].pt); if (d < 400.f || d > 8000) continue; keep_key_points.push_back(key_points[k]); keep_descriptors.push_back(descriptors.row(k)); keep_depths.push_back(d); } // wrap keypoints to cam_Frame and add in to cam_Frames cam_Frames[i] = (CamFrame) {i, keep_key_points,keep_depths}; descriptors_vec[i] = keep_descriptors; _log("Found %d key points in image %d.", keep_key_points.size(), i); } _log.tok(); }