示例#1
0
    void compare(File targetGallery, File queryGallery, File output)
    {
        qDebug("Comparing %s and %s%s", qPrintable(targetGallery.flat()),
               qPrintable(queryGallery.flat()),
               output.isNull() ? "" : qPrintable(" to " + output.flat()));

        // Escape hatch for distances that need to operate directly on the gallery files
        if (distance->compare(targetGallery, queryGallery, output))
            return;

        // Are we comparing the same gallery against itself?
        bool selfCompare = targetGallery == queryGallery;

        // Should we use multiple processes to do enrollment/comparison? If not, we just do multi-threading.
        bool multiProcess = Globals->file.getBool("multiProcess", false);

        // In comparing two galleries, we will keep the smaller one in memory, and load the larger one
        // incrementally. If the gallery set is larger than the probe set, we operate in transpose mode
        // i.e. we must transpose our output, to still write the output matrix in row-major order.
        bool transposeMode = false;

        // Is the larger gallery already enrolled? If not, we will enroll those images in-line with their
        // comparison against the smaller gallery (which will be enrolled, and stored in memory).
        bool needEnrollRows = false;

        if (output.exists() && output.get<bool>("cache", false)) return;
        if (queryGallery == ".") queryGallery = targetGallery;

        // To decide which gallery is larger, we need to read both, but at this point we just want the
        // metadata, and don't need the enrolled matrices.
        FileList targetMetadata;
        FileList queryMetadata;

        // Emptyread reads a gallery, and discards any matrices present, keeping only the metadata.
        targetMetadata = FileList::fromGallery(targetGallery, true);
        queryMetadata  = FileList::fromGallery(queryGallery, true);


        // Is the target or query set larger? We will use the larger as the rows of our comparison matrix (and transpose the output if necessary)
        transposeMode = targetMetadata.size() > queryMetadata.size();

        File rowGallery = queryGallery;
        File colGallery = targetGallery;
        qint64 rowSize;

        Gallery * temp;
        if (transposeMode)
        {
            rowGallery = targetGallery;
            colGallery = queryGallery;
            temp = Gallery::make(targetGallery);
        }
        else
        {
            temp = Gallery::make(queryGallery);
        }
        rowSize = temp->totalSize();
        delete temp;

        // Is the column gallery already enrolled? We keep the enrolled column gallery in memory, and in multi-process
        // mode, every worker process retains a copy of this gallery in memory. When not in multi-process mode, we can
        // simple make sure the enrolled data is stored in a memGallery, but in multi-process mode we save the enrolled
        // data to disk (as a .gal file) so that each worker process can read it without re-doing enrollment.
        File colEnrolledGallery = colGallery;
        QString targetExtension = multiProcess ? "gal" : "mem";

        // If the column gallery is not already of the appropriate type, we need to do something
        if (colGallery.suffix() != targetExtension)
        {
            // Build the name of a gallery containing the enrolled data, of the appropriate type.
            colEnrolledGallery = colGallery.baseName() + colGallery.hash() + (multiProcess ? ".gal" : ".mem");

            // Check if we have to do real enrollment, and not just convert the gallery's type.
            if (!(QStringList() << "gal" << "template" << "mem").contains(colGallery.suffix()))
            {
                enroll(colGallery, colEnrolledGallery);
            }
            // If the gallery does have enrolled templates, but is not the right type, we do a simple
            // type conversion for it.
            else
            {
                QScopedPointer<Gallery> readColGallery(Gallery::make(colGallery));
                TemplateList templates = readColGallery->read();
                QScopedPointer<Gallery> enrolledColOutput(Gallery::make(colEnrolledGallery));
                enrolledColOutput->writeBlock(templates);
            }
        }

        // We have handled the column gallery, now decide whehter or not we have to enroll the row gallery.
        if (selfCompare)
        {
            // For self-comparisons, we just use the already enrolled column set.
            rowGallery = colEnrolledGallery;
        }
        // Otherwise, we will need to enroll the row set. Since the actual comparison is defined via a transform
        // which compares incoming templates against a gallery, we will handle enrollment of the row set by simply
        // building a transform that does enrollment (using the current algorithm), then does the comparison in one
        // step. This way, we don't have to retain the complete enrolled row gallery in memory, or on disk.
        else if(!(QStringList() << "gal" << "mem" << "template").contains(rowGallery.suffix()))
        {
            needEnrollRows = true;
        }

        // At this point, we have decided how we will structure the comparison (either in transpose mode, or not), 
        // and have the column gallery enrolled, and have decided whether or not we need to enroll the row gallery.
        // From this point, we will build a single algorithm that (optionally) does enrollment, then does comparisons
        // and output, optionally using ProcessWrapper to do the enrollment and comparison in separate processes.
        //
        // There are two main components to this algorithm. The first is the (optional) enrollment and then the
        // comparison step (built from a GalleryCompare transform), and the second is the sequential matrix output and
        // progress counting step.
        // After the base algorithm is built, the whole thing will be run in a stream, so that I/O can be handled sequentially.

        // The actual comparison step is done by a GalleryCompare transform, which has a Distance, and a gallery as data.
        // Incoming templates are compared against the templates in the gallery, and the output is the resulting score
        // vector.
        QString compareRegionDesc = "Pipe([GalleryCompare("+Globals->algorithm + "," + colEnrolledGallery.flat() + ")])";


        QScopedPointer<Transform> compareRegion;
        // If we need to enroll the row set, we add the current algorithm's enrollment transform before the
        // GalleryCompare in a pipe.
        if (needEnrollRows)
        {
            if (!multiProcess)
            {
                compareRegionDesc = compareRegionDesc;
                compareRegion.reset(Transform::make(compareRegionDesc,NULL));
                CompositeTransform * downcast = dynamic_cast<CompositeTransform *> (compareRegion.data());
                if (downcast == NULL)
                    qFatal("Pipe downcast failed in compare");

                downcast->transforms.prepend(this->transform.data());
                downcast->init();
            }
            else
            {
                compareRegionDesc = "ProcessWrapper(" + this->transformString + "+" + compareRegionDesc + ")";
                compareRegion.reset(Transform::make(compareRegionDesc, NULL));
            }
        }
        else {
            if (multiProcess)
                compareRegionDesc = "ProcessWrapper(" + compareRegionDesc + ")";
            compareRegion.reset(Transform::make(compareRegionDesc,NULL));
        }

        // At this point, compareRegion is a transform, which optionally does enrollment, then compares the row
        // set against the column set. If in multi-process mode, the enrollment and comparison are wrapped in a 
        // ProcessWrapper transform, and will be transparently run in multiple processes.
        compareRegion->init();


        // We also need to add Output and progress counting to the algorithm we are building, so we will assign them to
        // two stages of a pipe.
        QString joinDesc = "Pipe()";
        QScopedPointer<Transform> join(Transform::make(joinDesc, NULL));

        // The output transform takes the metadata memGalleries we set up previously as input, along with the
        // output specification we were passed. Gallery metadata is necessary for some Outputs to function correctly.
        QString outputString = output.flat().isEmpty() ? "Empty" : output.flat();
        QString outputRegionDesc = "Output("+ outputString +"," + targetGallery.flat() +"," + queryGallery.flat() + ","+ QString::number(transposeMode ? 1 : 0) + ")";
        // The ProgressCounter transform will simply provide a display about the number of rows completed.
        outputRegionDesc += "+ProgressCounter("+QString::number(rowSize)+")+Discard";
        QScopedPointer<Transform> outputTform(Transform::make(outputRegionDesc, NULL));

        // Assign the comparison transform we previously built, and the output transform  we just built to
        // two stages of a pipe.
        CompositeTransform * downcast = dynamic_cast<CompositeTransform *> (join.data());
        downcast->transforms.append(compareRegion.data());
        downcast->transforms.append(outputTform.data());

        // With this, we have set up a transform which (optionally) enrolls templates, compares them
        // against a gallery, and outputs them.
        join->init();

        // Now, we will give that base transform to a stream, which will incrementally read the row gallery
        // and pass the transforms it reads through the base algorithm.
        QString streamDesc = "Stream(readMode=StreamGallery)";
        QScopedPointer<Transform> streamBase(Transform::make(streamDesc, NULL));
        WrapperTransform * streamWrapper = dynamic_cast<WrapperTransform *> (streamBase.data());
        streamWrapper->transform = join.data();

        // The transform we will use is now complete.
        streamWrapper->init();

        // We set up a template containing the rowGallery we want to compare. 
        TemplateList rowGalleryTemplate;
        rowGalleryTemplate.append(Template(rowGallery));
        TemplateList outputGallery;

        // Set up progress counting variables
        Globals->currentStep = 0;
        Globals->totalSteps = rowSize;
        Globals->startTime.start();

        // Do the actual comparisons
        streamWrapper->projectUpdate(rowGalleryTemplate, outputGallery);
    }