bool ImageBufAlgo::compare (const ImageBuf &A, const ImageBuf &B, float failthresh, float warnthresh, ImageBufAlgo::CompareResults &result, ROI roi, int nthreads) { // If no ROI is defined, use the union of the data windows of the two // images. if (! roi.defined()) roi = roi_union (get_roi(A.spec()), get_roi(B.spec())); roi.chend = std::min (roi.chend, std::max(A.nchannels(), B.nchannels())); // Deep and non-deep images cannot be compared if (B.deep() != A.deep()) return false; bool ok; OIIO_DISPATCH_TYPES2 (ok, "compare", compare_, A.spec().format, B.spec().format, A, B, failthresh, warnthresh, result, roi, nthreads); // FIXME - The nthreads argument is for symmetry with the rest of // ImageBufAlgo and for future expansion. But for right now, we // don't actually split by threads. Maybe later. return ok; }
/// Fix all non-finite pixels (nan/inf) using the specified approach bool ImageBufAlgo::fixNonFinite (ImageBuf &src, NonFiniteFixMode mode, int *pixelsFixed, ROI roi, int nthreads) { // If no ROI is defined, use the data window of src. if (! roi.defined()) roi = get_roi(src.spec()); roi.chend = std::min (roi.chend, src.nchannels()); // Initialize if (pixelsFixed) *pixelsFixed = 0; switch (src.spec().format.basetype) { case TypeDesc::FLOAT : return fixNonFinite_<float> (src, mode, pixelsFixed, roi, nthreads); case TypeDesc::HALF : return fixNonFinite_<half> (src, mode, pixelsFixed, roi, nthreads); case TypeDesc::DOUBLE: return fixNonFinite_<double> (src, mode, pixelsFixed, roi, nthreads); default: // All other format types aren't capable of having nonfinite // pixel values. return true; } }
bool ImageBufAlgo::transpose (ImageBuf &dst, const ImageBuf &src, ROI roi, int nthreads) { pvt::LoggedTimer logtime("IBA::transpose"); if (! roi.defined()) roi = get_roi (src.spec()); roi.chend = std::min (roi.chend, src.nchannels()); ROI dst_roi (roi.ybegin, roi.yend, roi.xbegin, roi.xend, roi.zbegin, roi.zend, roi.chbegin, roi.chend); bool dst_initialized = dst.initialized(); if (! IBAprep (dst_roi, &dst)) return false; if (! dst_initialized) { ROI r = src.roi_full(); ROI dst_roi_full (r.ybegin, r.yend, r.xbegin, r.xend, r.zbegin, r.zend, r.chbegin, r.chend); dst.set_roi_full (dst_roi_full); } bool ok; if (dst.spec().format == src.spec().format) { OIIO_DISPATCH_TYPES (ok, "transpose", transpose_, dst.spec().format, dst, src, roi, nthreads); } else { OIIO_DISPATCH_COMMON_TYPES2 (ok, "transpose", transpose_, dst.spec().format, src.spec().format, dst, src, roi, nthreads); } return ok; }
bool ImageBufAlgo::transpose (ImageBuf &dst, const ImageBuf &src, ROI roi, int nthreads) { if (! roi.defined()) roi = get_roi (src.spec()); roi.chend = std::min (roi.chend, src.nchannels()); ROI dst_roi (roi.ybegin, roi.yend, roi.xbegin, roi.xend, roi.zbegin, roi.zend, roi.chbegin, roi.chend); IBAprep (dst_roi, &dst); OIIO_DISPATCH_TYPES2 ("transpose", transpose_, dst.spec().format, src.spec().format, dst, src, roi, nthreads); return false; }
bool ImageBufAlgo::isMonochrome (const ImageBuf &src, ROI roi, int nthreads) { // If no ROI is defined, use the data window of src. if (! roi.defined()) roi = get_roi(src.spec()); roi.chend = std::min (roi.chend, src.nchannels()); if (roi.nchannels() < 2) return true; // 1 or fewer channels are always "monochrome" OIIO_DISPATCH_TYPES ("isMonochrome", isMonochrome_, src.spec().format, src, roi, nthreads); // FIXME - The nthreads argument is for symmetry with the rest of // ImageBufAlgo and for future expansion. But for right now, we // don't actually split by threads. Maybe later. };
bool ImageBufAlgo::color_count (const ImageBuf &src, imagesize_t *count, int ncolors, const float *color, const float *eps, ROI roi, int nthreads) { // If no ROI is defined, use the data window of src. if (! roi.defined()) roi = get_roi(src.spec()); roi.chend = std::min (roi.chend, src.nchannels()); if (! eps) { float *localeps = ALLOCA (float, roi.chend); for (int c = 0; c < roi.chend; ++c) localeps[c] = 0.001f; eps = localeps; }
static bool convolve_ (ImageBuf &dst, const ImageBuf &src, const ImageBuf &kernel, bool normalize, ROI roi, int nthreads) { if (nthreads != 1 && roi.npixels() >= 1000) { // Lots of pixels and request for multi threads? Parallelize. ImageBufAlgo::parallel_image ( boost::bind(convolve_<DSTTYPE,SRCTYPE>, boost::ref(dst), boost::cref(src), boost::cref(kernel), normalize, _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } // Serial case float scale = 1.0f; if (normalize) { scale = 0.0f; for (ImageBuf::ConstIterator<float> k (kernel); ! k.done(); ++k) scale += k[0]; scale = 1.0f / scale; } float *sum = ALLOCA (float, roi.chend); ROI kroi = get_roi (kernel.spec()); ImageBuf::Iterator<DSTTYPE> d (dst, roi); ImageBuf::ConstIterator<SRCTYPE> s (src, roi, ImageBuf::WrapClamp); for ( ; ! d.done(); ++d) { for (int c = roi.chbegin; c < roi.chend; ++c) sum[c] = 0.0f; for (ImageBuf::ConstIterator<float> k (kernel, kroi); !k.done(); ++k) { float kval = k[0]; s.pos (d.x() + k.x(), d.y() + k.y(), d.z() + k.z()); for (int c = roi.chbegin; c < roi.chend; ++c) sum[c] += kval * s[c]; } for (int c = roi.chbegin; c < roi.chend; ++c) d[c] = scale * sum[c]; } return true; }
bool ImageBufAlgo::isConstantChannel (const ImageBuf &src, int channel, float val, ROI roi, int nthreads) { // If no ROI is defined, use the data window of src. if (! roi.defined()) roi = get_roi(src.spec()); if (channel < 0 || channel >= src.nchannels()) return false; // that channel doesn't exist in the image OIIO_DISPATCH_TYPES ("isConstantChannel", isConstantChannel_, src.spec().format, src, channel, val, roi, nthreads); // FIXME - The nthreads argument is for symmetry with the rest of // ImageBufAlgo and for future expansion. But for right now, we // don't actually split by threads. Maybe later. };
bool ImageBufAlgo::isConstantColor (const ImageBuf &src, float *color, ROI roi, int nthreads) { // If no ROI is defined, use the data window of src. if (! roi.defined()) roi = get_roi(src.spec()); roi.chend = std::min (roi.chend, src.nchannels()); if (roi.nchannels() == 0) return true; OIIO_DISPATCH_TYPES ("isConstantColor", isConstantColor_, src.spec().format, src, color, roi, nthreads); // FIXME - The nthreads argument is for symmetry with the rest of // ImageBufAlgo and for future expansion. But for right now, we // don't actually split by threads. Maybe later. };
bool ImageBufAlgo::computePixelStats (PixelStats &stats, const ImageBuf &src, ROI roi, int nthreads) { if (! roi.defined()) roi = get_roi (src.spec()); else roi.chend = std::min (roi.chend, src.nchannels()); int nchannels = src.spec().nchannels; if (nchannels == 0) { src.error ("%d-channel images not supported", nchannels); return false; } OIIO_DISPATCH_TYPES ("computePixelStats", computePixelStats_, src.spec().format, src, stats, roi, nthreads); return false; }
bool ImageBufAlgo::channel_sum (ImageBuf &dst, const ImageBuf &src, const float *weights, ROI roi, int nthreads) { if (! roi.defined()) roi = get_roi(src.spec()); roi.chend = std::min (roi.chend, src.nchannels()); ROI dstroi = roi; dstroi.chbegin = 0; dstroi.chend = 1; if (! IBAprep (dstroi, &dst)) return false; if (! weights) { float *local_weights = ALLOCA (float, roi.chend); for (int c = 0; c < roi.chend; ++c) local_weights[c] = 1.0f; weights = &local_weights[0]; }
bool ImageBufAlgo::paste (ImageBuf &dst, int xbegin, int ybegin, int zbegin, int chbegin, const ImageBuf &src, ROI srcroi, int nthreads) { if (! srcroi.defined()) srcroi = get_roi(src.spec()); ROI dstroi (xbegin, xbegin+srcroi.width(), ybegin, ybegin+srcroi.height(), zbegin, zbegin+srcroi.depth(), chbegin, chbegin+srcroi.nchannels()); ROI dstroi_save = dstroi; // save the original IBAprep (dstroi, &dst); // do the actual copying OIIO_DISPATCH_TYPES2 ("paste", paste_, dst.spec().format, src.spec().format, dst, dstroi_save, src, srcroi, nthreads); return false; }
bool ImageBufAlgo::histogram (const ImageBuf &A, int channel, std::vector<imagesize_t> &histogram, int bins, float min, float max, imagesize_t *submin, imagesize_t *supermax, ROI roi) { if (A.spec().format != TypeDesc::TypeFloat) { A.error ("Unsupported pixel data format '%s'", A.spec().format); return false; } if (A.nchannels() == 0) { A.error ("Input image must have at least 1 channel"); return false; } if (channel < 0 || channel >= A.nchannels()) { A.error ("Invalid channel %d for input image with channels 0 to %d", channel, A.nchannels()-1); return false; } if (bins < 1) { A.error ("The number of bins must be at least 1"); return false; } if (max <= min) { A.error ("Invalid range, min must be strictly smaller than max"); return false; } // Specified ROI -> use it. Unspecified ROI -> initialize from A. if (! roi.defined()) roi = get_roi (A.spec()); histogram_impl<float> (A, channel, histogram, bins, min, max, submin, supermax, roi); return ! A.has_error(); }
bool ImageBufAlgo::transpose (ImageBuf &dst, const ImageBuf &src, ROI roi, int nthreads) { if (! roi.defined()) roi = get_roi (src.spec()); roi.chend = std::min (roi.chend, src.nchannels()); ROI dst_roi (roi.ybegin, roi.yend, roi.xbegin, roi.xend, roi.zbegin, roi.zend, roi.chbegin, roi.chend); bool dst_initialized = dst.initialized(); IBAprep (dst_roi, &dst); if (! dst_initialized) { ROI r = src.roi_full(); ROI dst_roi_full (r.ybegin, r.yend, r.xbegin, r.xend, r.zbegin, r.zend, r.chbegin, r.chend); dst.set_roi_full (dst_roi_full); } bool ok; OIIO_DISPATCH_TYPES2 (ok, "transpose", transpose_, dst.spec().format, src.spec().format, dst, src, roi, nthreads); return ok; }
bool ImageBufAlgo::zover (ImageBuf &R, const ImageBuf &A, const ImageBuf &B, bool z_zeroisinf, ROI roi, int nthreads) { const ImageSpec &specR = R.spec(); const ImageSpec &specA = A.spec(); const ImageSpec &specB = B.spec(); int nchannels_R, nchannels_A, nchannels_B; int alpha_R, alpha_A, alpha_B; int z_R, z_A, z_B; int colors_R, colors_A, colors_B; bool initialized_R = decode_over_channels (R, nchannels_R, alpha_R, z_R, colors_R); bool initialized_A = decode_over_channels (A, nchannels_A, alpha_A, z_A, colors_A); bool initialized_B = decode_over_channels (B, nchannels_B, alpha_B, z_B, colors_B); if (! initialized_A || ! initialized_B) { R.error ("Can't 'zover' uninitialized images"); return false; } // Fail if the input images don't have a Z channel. if (z_A < 0 || z_B < 0 || (initialized_R && z_R < 0)) { R.error ("'zover' requires Z channels"); return false; } // Fail if the input images don't have an alpha channel. if (alpha_A < 0 || alpha_B < 0 || (initialized_R && alpha_R < 0)) { R.error ("'zover' requires alpha channels"); return false; } // Fail for mismatched channel counts if (colors_A != colors_B || colors_A < 1) { R.error ("Can't 'zover' images with mismatched color channel counts (%d vs %d)", colors_A, colors_B); return false; } // Fail for unaligned alpha or z channels if (alpha_A != alpha_B || z_A != z_B || (initialized_R && alpha_R != alpha_A) || (initialized_R && z_R != z_A)) { R.error ("Can't 'zover' images with mismatched channel order", colors_A, colors_B); return false; } // At present, this operation only supports ImageBuf's containing // float pixel data. if ((initialized_R && specR.format != TypeDesc::TypeFloat) || specA.format != TypeDesc::TypeFloat || specB.format != TypeDesc::TypeFloat) { R.error ("Unsupported pixel data format combination '%s = %s zover %s'", specR.format, specA.format, specB.format); return false; } // Uninitialized R -> size it to the union of A and B. if (! initialized_R) { ImageSpec newspec = specA; set_roi (newspec, roi_union (get_roi(specA), get_roi(specB))); R.reset ("zover", newspec); } // Specified ROI -> use it. Unspecified ROI -> initialize from R. if (! roi.defined()) roi = get_roi (R.spec()); parallel_image (boost::bind (over_impl<float,float,float>, boost::ref(R), boost::cref(A), boost::cref(B), _1, true, z_zeroisinf), roi, nthreads); return ! R.has_error(); }
std::string ImageBufAlgo::computePixelHashSHA1 (const ImageBuf &src, const std::string & extrainfo, ROI roi, int blocksize, int nthreads) { if (! roi.defined()) roi = get_roi (src.spec()); // Fall back to whole-image hash for only one block if (blocksize <= 0 || blocksize >= roi.height()) return simplePixelHashSHA1 (src, extrainfo, roi); // Request for 0 threads means "use the OIIO global thread count" if (nthreads <= 0) OIIO::getattribute ("threads", nthreads); int nblocks = (roi.height()+blocksize-1) / blocksize; std::vector<std::string> results (nblocks); if (nthreads <= 1) { sha1_hasher (&src, roi, blocksize, &results[0], 0); } else { // parallel case boost::thread_group threads; int blocks_per_thread = (nblocks+nthreads-1) / nthreads; ROI broi = roi; for (int b = 0, t = 0; b < nblocks; b += blocks_per_thread, ++t) { int y = roi.ybegin + b*blocksize; if (y >= roi.yend) break; broi.ybegin = y; broi.yend = std::min (y+blocksize*blocks_per_thread, roi.yend); threads.add_thread (new boost::thread (sha1_hasher, &src, broi, blocksize, &results[0], b)); } threads.join_all (); } #ifdef USE_OPENSSL // If OpenSSL was available at build time, use its SHA-1 // implementation, which is about 20% faster than CSHA1. SHA_CTX sha; SHA1_Init (&sha); for (int b = 0; b < nblocks; ++b) SHA1_Update (&sha, results[b].c_str(), results[b].size()); if (extrainfo.size()) SHA1_Update (&sha, extrainfo.c_str(), extrainfo.size()); unsigned char md[SHA_DIGEST_LENGTH]; char hash_digest[2*SHA_DIGEST_LENGTH+1]; SHA1_Final (md, &sha); for (int i = 0; i < SHA_DIGEST_LENGTH; ++i) sprintf (hash_digest+2*i, "%02X", (int)md[i]); hash_digest[2*SHA_DIGEST_LENGTH] = 0; return std::string (hash_digest); #else // Fall back on CSHA1 if OpenSSL was not available or if CSHA1 sha; sha.Reset (); for (int b = 0; b < nblocks; ++b) sha.Update ((const unsigned char *)results[b].c_str(), results[b].size()); if (extrainfo.size()) sha.Update ((const unsigned char *)extrainfo.c_str(), extrainfo.size()); sha.Final (); std::string hash_digest; sha.ReportHashStl (hash_digest, CSHA1::REPORT_HEX_SHORT); return hash_digest; #endif }
OIIO_NAMESPACE_BEGIN bool ImageBufAlgo::IBAprep (ROI &roi, ImageBuf *dst, const ImageBuf *A, const ImageBuf *B, const ImageBuf *C, ImageSpec *force_spec, int prepflags) { if ((A && !A->initialized()) || (B && !B->initialized()) || (C && !C->initialized())) { if (dst) dst->error ("Uninitialized input image"); return false; } int maxchans = 10000; if (prepflags & IBAprep_CLAMP_MUTUAL_NCHANNELS) { // Instructions to clamp chend to the highest of the inputs if (dst && dst->initialized()) maxchans = std::min (maxchans, dst->spec().nchannels); if (A && A->initialized()) maxchans = std::min (maxchans, A->spec().nchannels); if (B && B->initialized()) maxchans = std::min (maxchans, B->spec().nchannels); if (C && C->initialized()) maxchans = std::min (maxchans, C->spec().nchannels); } if (dst->initialized()) { // Valid destination image. Just need to worry about ROI. if (roi.defined()) { // Shrink-wrap ROI to the destination (including chend) roi = roi_intersection (roi, get_roi(dst->spec())); } else { // No ROI? Set it to all of dst's pixel window. roi = get_roi (dst->spec()); } // If the dst is initialized but is a cached image, we'll need // to fully read it into allocated memory so that we're able // to write to it subsequently. dst->make_writeable (true); } else { // Not an initialized destination image! ASSERT ((A || roi.defined()) && "ImageBufAlgo without any guess about region of interest"); ROI full_roi; if (! roi.defined()) { // No ROI -- make it the union of the pixel regions of the inputs roi = A->roi(); full_roi = A->roi_full(); if (B) { roi = roi_union (roi, B->roi()); full_roi = roi_union (full_roi, B->roi_full()); } if (C) { roi = roi_union (roi, C->roi()); full_roi = roi_union (full_roi, C->roi_full()); } } else { if (A) { roi.chend = std::min (roi.chend, A->nchannels()); if (! (prepflags & IBAprep_NO_COPY_ROI_FULL)) full_roi = A->roi_full(); } else { full_roi = roi; } } // Now we allocate space for dst. Give it A's spec, but adjust // the dimensions to match the ROI. ImageSpec spec; if (A) { // If there's an input image, give dst A's spec (with // modifications detailed below...) spec = force_spec ? (*force_spec) : A->spec(); // For multiple inputs, if they aren't the same data type, punt and // allocate a float buffer. If the user wanted something else, // they should have pre-allocated dst with their desired format. if (B && A->spec().format != B->spec().format) spec.set_format (TypeDesc::FLOAT); if (C && (A->spec().format != C->spec().format || B->spec().format != C->spec().format)) spec.set_format (TypeDesc::FLOAT); // No good can come from automatically polluting an ImageBuf // with some other ImageBuf's tile sizes. spec.tile_width = 0; spec.tile_height = 0; spec.tile_depth = 0; } else if (force_spec) { spec = *force_spec; } else { spec.set_format (TypeDesc::FLOAT); spec.nchannels = roi.chend; spec.default_channel_names (); } // Set the image dimensions based on ROI. set_roi (spec, roi); if (full_roi.defined()) set_roi_full (spec, full_roi); else set_roi_full (spec, roi); if (prepflags & IBAprep_NO_COPY_METADATA) spec.extra_attribs.clear(); else if (! (prepflags & IBAprep_COPY_ALL_METADATA)) { // Since we're altering pixels, be sure that any existing SHA // hash of dst's pixel values is erased. spec.erase_attribute ("oiio:SHA-1"); static boost::regex regex_sha ("SHA-1=[[:xdigit:]]*[ ]*"); std::string desc = spec.get_string_attribute ("ImageDescription"); if (desc.size()) spec.attribute ("ImageDescription", boost::regex_replace (desc, regex_sha, "")); } dst->reset (spec); } roi.chend = std::min (roi.chend, maxchans); if (prepflags & IBAprep_REQUIRE_ALPHA) { if (dst->spec().alpha_channel < 0 || (A && A->spec().alpha_channel < 0) || (B && B->spec().alpha_channel < 0) || (C && C->spec().alpha_channel < 0)) { dst->error ("images must have alpha channels"); return false; } } if (prepflags & IBAprep_REQUIRE_Z) { if (dst->spec().z_channel < 0 || (A && A->spec().z_channel < 0) || (B && B->spec().z_channel < 0) || (C && C->spec().z_channel < 0)) { dst->error ("images must have depth channels"); return false; } } if (prepflags & IBAprep_REQUIRE_SAME_NCHANNELS) { int n = dst->spec().nchannels; if ((A && A->spec().nchannels != n) || (B && B->spec().nchannels != n) || (C && C->spec().nchannels != n)) { dst->error ("images must have the same number of channels"); return false; } } if (prepflags & IBAprep_NO_SUPPORT_VOLUME) { if (dst->spec().depth > 1 || (A && A->spec().depth > 1) || (B && B->spec().depth > 1) || (C && C->spec().depth > 1)) { dst->error ("volumes not supported"); return false; } } if (! (prepflags & IBAprep_SUPPORT_DEEP) && ((dst && dst->deep()) || (A && A->deep()) || (B && B->deep()) || (C && C->deep()))) { dst->error ("deep data not supported"); return false; } return true; }
static bool computePixelStats_ (const ImageBuf &src, ImageBufAlgo::PixelStats &stats, ROI roi, int nthreads) { if (! roi.defined()) roi = get_roi (src.spec()); else roi.chend = std::min (roi.chend, src.nchannels()); int nchannels = src.spec().nchannels; // Use local storage for smaller batches, then merge the batches // into the final results. This preserves precision for large // images, where the running total may be too big to incorporate the // contributions of individual pixel values without losing // precision. // // This approach works best when the batch size is the sqrt of // numpixels, which makes the num batches roughly equal to the // number of pixels / batch. ImageBufAlgo::PixelStats tmp; reset (tmp, nchannels); reset (stats, nchannels); int PIXELS_PER_BATCH = std::max (1024, static_cast<int>(sqrt((double)src.spec().image_pixels()))); if (src.deep()) { // Loop over all pixels ... for (ImageBuf::ConstIterator<T> s(src, roi); ! s.done(); ++s) { int samples = s.deep_samples(); if (! samples) continue; for (int c = roi.chbegin; c < roi.chend; ++c) { for (int i = 0; i < samples; ++i) { float value = s.deep_value (c, i); val (tmp, c, value); if ((tmp.finitecount[c] % PIXELS_PER_BATCH) == 0) { merge (stats, tmp); reset (tmp, nchannels); } } } } } else { // Non-deep case // Loop over all pixels ... for (ImageBuf::ConstIterator<T> s(src, roi); ! s.done(); ++s) { for (int c = roi.chbegin; c < roi.chend; ++c) { float value = s[c]; val (tmp, c, value); if ((tmp.finitecount[c] % PIXELS_PER_BATCH) == 0) { merge (stats, tmp); reset (tmp, nchannels); } } } } // Merge anything left over merge (stats, tmp); // Compute final results finalize (stats); return ! src.has_error(); };
void Predator::initialLearning() { //this->detectedBB = this->detector->detect(this->currImg, this->integralImg); Mat target = get_roi(this->currImg,this->currBB); this->objModel.label = this->label; this->objModel.load(target); this->detector->featureTracker->init(this->objModel); NormalizedPatch patch = this->currBB.extractNormalizedPatch(this->currImg); patch.positive = 1; float initVar = patch.var();//this->detector->varianceFilter->computeVar(this->integralImg,this->integralImgSqr, this->currBB); detector->varianceFilter->minVar = initVar/3.0; float overlaps[this->detector->windows.size()]; this->currBB.overlap(this->detector->windows, overlaps); //Add all bounding boxes with high overlap vector< pair<int,float> > positiveIndices; vector<int> negativeIndices; //First: Find overlapping positive and negative patches for(int i = 0; i < this->detector->windows.size(); i++) { if(overlaps[i] > 0.6f) { positiveIndices.push_back(pair<int,float>(i,overlaps[i])); } if(overlaps[i] < 0.1f) { negativeIndices.push_back(i); } } sort(positiveIndices.begin(), positiveIndices.end(), tldSortByOverlapDesc); vector<NormalizedPatch> patches; patches.push_back(patch); //Add first patch to patch list int numIterations = std::min<size_t>(positiveIndices.size(), 10); //Take at most 10 bounding boxes (sorted by overlap) for(int i = 0; i < numIterations; i++) { int idx = positiveIndices.at(i).first; //Learn this bounding box //TODO: Somewhere here image warping might be possible this->detector->ffClassifier->train(this->integralImg, this->detector->windows[idx], true); } srand(time(NULL)); //TODO: This is not guaranteed to affect random_shuffle random_shuffle(negativeIndices.begin(), negativeIndices.end()); //Choose 100 random patches for negative examples for(size_t i = 0; i < std::min<size_t>(100,negativeIndices.size()); i++) { int idx = negativeIndices.at(i); NormalizedPatch patch = this->detector->windows[idx].extractNormalizedPatch(this->currImg); patch.positive = 0; patches.push_back( patch ); } this->detector->nnClassifier->learn(patches); }