Пример #1
0
RectI
TrackMarker::getMarkerImageRoI(int time) const
{
    const unsigned int mipmapLevel = 0;
    Point center, offset;
    KnobDoublePtr centerKnob = getCenterKnob();
    KnobDoublePtr offsetKnob = getOffsetKnob();

    center.x = centerKnob->getValueAtTime(time, 0);
    center.y = centerKnob->getValueAtTime(time, 1);

    offset.x = offsetKnob->getValueAtTime(time, 0);
    offset.y = offsetKnob->getValueAtTime(time, 1);

    RectD roiCanonical;
    KnobDoublePtr swBl = getSearchWindowBottomLeftKnob();
    KnobDoublePtr swTr = getSearchWindowTopRightKnob();

    roiCanonical.x1 = swBl->getValueAtTime(time, 0) + center.x + offset.x;
    roiCanonical.y1 = swBl->getValueAtTime(time, 1) + center.y + offset.y;
    roiCanonical.x2 = swTr->getValueAtTime(time, 0) + center.x + offset.x;
    roiCanonical.y2 = swTr->getValueAtTime(time, 1) + center.y + offset.y;

    RectI roi;
    NodePtr node = getContext()->getNode();
    NodePtr input = node->getInput(0);
    if (!input) {
        return RectI();
    }
    roiCanonical.toPixelEnclosing(mipmapLevel, input ? input->getEffectInstance()->getAspectRatio(-1) : 1., &roi);

    return roi;
}
Пример #2
0
ActionRetCodeEnum
RotoShapeRenderNode::isIdentity(TimeValue time,
                                const RenderScale & scale,
                                const RectI & roi,
                                ViewIdx view,
                                const ImagePlaneDesc& /*plane*/,
                                TimeValue* inputTime,
                                ViewIdx* inputView,
                                int* inputNb,
                                ImagePlaneDesc* /*inputPlane*/)
{
    *inputView = view;
    NodePtr node = getNode();

    *inputNb = -1;
    RotoDrawableItemPtr rotoItem = getAttachedRotoItem();
    if (!rotoItem) {
        return eActionStatusFailed;
    }
    Bezier* isBezier = dynamic_cast<Bezier*>(rotoItem.get());
    if (!rotoItem || !rotoItem->isActivated(time, view) || (isBezier && ((!isBezier->isCurveFinished(view) && !isBezier->isOpenBezier()) || isBezier->getControlPointsCount(view) <= 1))) {
        *inputTime = time;
        *inputNb = 0;

        return eActionStatusOK;
    }

    bool isPainting = isDuringPaintStrokeCreation();
    RectD maskRod;
    getRoDFromItem(rotoItem, time, view, isPainting, &maskRod);

    RectI maskPixelRod;
    maskRod.toPixelEnclosing(scale, getAspectRatio(-1), &maskPixelRod);
    if ( !maskPixelRod.intersects(roi) ) {
        *inputTime = time;
        *inputNb = 0;
    }
    
    return eActionStatusOK;
} // isIdentity
Пример #3
0
void
ViewerRenderFrameSubResult::onTreeRenderFinished(int inputIndex)
{
    PerViewerInputRenderData& inputData = perInputsData[inputIndex];

    if (inputIndex == 1 && copyInputBFromA) {
        inputData = perInputsData[0];
        return;
    }

    FrameViewRequestPtr outputRequest;
    if (inputData.render) {
        inputData.retCode = inputData.render->getStatus();
        outputRequest = inputData.render->getOutputRequest();
    }
    if (outputRequest) {
        inputData.viewerProcessImage = outputRequest->getRequestedScaleImagePlane();
    }

    // There might be no output image if the RoI that was passed to render is outside of the RoD of the effect
    if (isFailureRetCode(inputData.retCode) || !inputData.viewerProcessImage) {
        inputData.viewerProcessImage.reset();
        inputData.render.reset();
        return;
    }


    // Find the key of the image and store it so that in the gui
    // we can later on re-use this key to check the cache for the timeline's cache line
    ImageCacheEntryPtr cacheEntry = inputData.viewerProcessImage->getCacheEntry();
    if (cacheEntry) {
        inputData.viewerProcessImageKey = cacheEntry->getCacheKey();
    }

    // Convert the image to a format that can be uploaded to a OpenGL texture


    RectI imageConvertRoI;
    RectD ctorCanonicalRoI = inputData.render->getCtorRoI();
    if (inputData.render && !ctorCanonicalRoI.isNull()) {
        RenderScale scale = EffectInstance::getCombinedScale(inputData.viewerProcessImage->getMipMapLevel(), inputData.viewerProcessImage->getProxyScale());
        double par = inputData.viewerProcessNode->getEffectInstance()->getAspectRatio(-1);
        ctorCanonicalRoI.toPixelEnclosing(scale, par, &imageConvertRoI);
    } else {
        imageConvertRoI = inputData.viewerProcessImage->getBounds();
    }

    // If we are drawing with the RotoPaint node, only update the texture portion
    if (textureTransferType == OpenGLViewerI::TextureTransferArgs::eTextureTransferTypeModify) {
        RectI strokeArea;
        bool strokeAreaSet = inputData.render->getRotoPaintActiveStrokeUpdateArea(&strokeArea);
        if (strokeAreaSet) {
            imageConvertRoI = strokeArea;
        }
    }

    // The viewer-process node may not have rendered a 4 channel image, but this is required but the OpenGL viewer
    // which only draws RGBA images.

    // If we are in accumulation, force a copy of the image because another render thread might modify it in a future render whilst it may
    // still be read from the main-thread when updating the ViewerGL texture.
    // If texture transfer is eTextureTransferTypeOverlay, we want to upload the texture to exactly what was requested
    const bool forceOutputImageCopy = (inputData.viewerProcessImage == inputData.viewerProcessNode->getEffectInstance()->getAccumBuffer(inputData.viewerProcessImage->getLayer()) ||
                                       (textureTransferType == OpenGLViewerI::TextureTransferArgs::eTextureTransferTypeOverlay && inputData.viewerProcessImage->getBounds() != imageConvertRoI));
    inputData.viewerProcessImage = convertImageForViewerDisplay(imageConvertRoI, forceOutputImageCopy, true /*the texture must have 4 channels*/, inputData.viewerProcessImage);

    // Extra color-picker images as-well.
    if (inputData.colorPickerNode) {
        {
            FrameViewRequestPtr req = inputData.render->getExtraRequestedResultsForNode(inputData.colorPickerNode);
            if (req) {
                inputData.colorPickerImage = req->getRequestedScaleImagePlane();
                if (inputData.colorPickerImage) {
                    inputData.colorPickerImage = convertImageForViewerDisplay(inputData.colorPickerImage->getBounds(), false, false /*the picker can accept non 4-channel image*/, inputData.colorPickerImage);
                }
            }
        }
        if (inputData.colorPickerInputNode) {
            FrameViewRequestPtr req = inputData.render->getExtraRequestedResultsForNode(inputData.colorPickerInputNode);
            if (req) {
                inputData.colorPickerInputImage = req->getRequestedScaleImagePlane();
                if (inputData.colorPickerInputImage) {
                    inputData.colorPickerInputImage = convertImageForViewerDisplay(inputData.colorPickerInputImage->getBounds(), false, false /*the picker can accept non 4-channel image*/, inputData.colorPickerInputImage);
                }
            }
        }
    }

    inputData.render.reset();
} // onTreeRenderFinished
Пример #4
0
/*
 * @brief This is called by LibMV to retrieve an image either for reference or as search frame.
 */
mv::FrameAccessor::Key
TrackerFrameAccessor::GetImage(int /*clip*/,
                               int frame,
                               mv::FrameAccessor::InputMode input_mode,
                               int downscale,            // Downscale by 2^downscale.
                               const mv::Region* region,     // Get full image if NULL.
                               const mv::FrameAccessor::Transform* /*transform*/, // May be NULL.
                               mv::FloatImage** destination)
{
    // Since libmv only uses MONO images for now we have only optimized for this case, remove and handle properly
    // other case(s) when they get integrated into libmv.
    assert(input_mode == mv::FrameAccessor::MONO);


    FrameAccessorCacheKey key;
    key.frame = frame;
    key.mipMapLevel = downscale;
    key.mode = input_mode;

    /*
       Check if a frame exists in the cache with matching key and bounds enclosing the given region
     */
    RectI roi;
    if (region) {
        convertLibMVRegionToRectI(*region, _imp->formatHeight, &roi);

        QMutexLocker k(&_imp->cacheMutex);
        std::pair<FrameAccessorCache::iterator, FrameAccessorCache::iterator> range = _imp->cache.equal_range(key);
        for (FrameAccessorCache::iterator it = range.first; it != range.second; ++it) {
            if ( (roi.x1 >= it->second.bounds.x1) && (roi.x2 <= it->second.bounds.x2) &&
                 ( roi.y1 >= it->second.bounds.y1) && ( roi.y2 <= it->second.bounds.y2) ) {
#ifdef TRACE_LIB_MV
                qDebug() << QThread::currentThread() << "FrameAccessor::GetImage():" << "Found cached image at frame" << frame << "with RoI x1="
                         << region->min(0) << "y1=" << region->max(1) << "x2=" << region->max(0) << "y2=" << region->min(1);
#endif
                // LibMV is kinda dumb on this we must necessarily copy the data either via CopyFrom or the
                // assignment constructor:
                // EDIT: fixed libmv
                *destination = it->second.image.get();
                //destination->CopyFrom<float>(*it->second.image);
                ++it->second.referenceCount;

                return (mv::FrameAccessor::Key)it->second.image.get();
            }
        }
    }

    EffectInstancePtr effect;
    if (_imp->trackerInput) {
        effect = _imp->trackerInput->getEffectInstance();
    }
    if (!effect) {
        return (mv::FrameAccessor::Key)0;
    }

    // Not in accessor cache, call renderRoI
    RenderScale scale;
    scale.y = scale.x = Image::getScaleFromMipMapLevel( (unsigned int)downscale );


    RectD precomputedRoD;
    if (!region) {
        bool isProjectFormat;
        StatusEnum stat = effect->getRegionOfDefinition_public(_imp->trackerInput->getHashValue(), frame, scale, ViewIdx(0), &precomputedRoD, &isProjectFormat);
        if (stat == eStatusFailed) {
            return (mv::FrameAccessor::Key)0;
        }
        double par = effect->getAspectRatio(-1);
        precomputedRoD.toPixelEnclosing( (unsigned int)downscale, par, &roi );
    }

    std::list<ImageComponents> components;
    components.push_back( ImageComponents::getRGBComponents() );

    NodePtr node = _imp->context->getNode();
    const bool isRenderUserInteraction = true;
    const bool isSequentialRender = false;
    AbortableRenderInfoPtr abortInfo = AbortableRenderInfo::create(false, 0);
    AbortableThread* isAbortable = dynamic_cast<AbortableThread*>( QThread::currentThread() );
    if (isAbortable) {
        isAbortable->setAbortInfo( isRenderUserInteraction, abortInfo, node->getEffectInstance() );
    }


    ParallelRenderArgsSetter::CtorArgsPtr tlsArgs(new ParallelRenderArgsSetter::CtorArgs);
    tlsArgs->time = frame;
    tlsArgs->view = ViewIdx(0);
    tlsArgs->isRenderUserInteraction = isRenderUserInteraction;
    tlsArgs->isSequential = isSequentialRender;
    tlsArgs->abortInfo = abortInfo;
    tlsArgs->treeRoot = node;
    tlsArgs->textureIndex = 0;
    tlsArgs->timeline = node->getApp()->getTimeLine();
    tlsArgs->activeRotoPaintNode = NodePtr();
    tlsArgs->activeRotoDrawableItem = RotoDrawableItemPtr();
    tlsArgs->isDoingRotoNeatRender = false;
    tlsArgs->isAnalysis = true;
    tlsArgs->draftMode = false;
    tlsArgs->stats = RenderStatsPtr();
    ParallelRenderArgsSetter frameRenderArgs(tlsArgs); // Stats
    EffectInstance::RenderRoIArgs args( frame,
                                        scale,
                                        downscale,
                                        ViewIdx(0),
                                        false,
                                        roi,
                                        precomputedRoD,
                                        components,
                                        eImageBitDepthFloat,
                                        true,
                                        _imp->context->getNode()->getEffectInstance(),
                                        eStorageModeRAM /*returnOpenGLTex*/,
                                        frame);
    std::map<ImageComponents, ImagePtr> planes;
    EffectInstance::RenderRoIRetCode stat = effect->renderRoI(args, &planes);
    if ( (stat != EffectInstance::eRenderRoIRetCodeOk) || planes.empty() ) {
#ifdef TRACE_LIB_MV
        qDebug() << QThread::currentThread() << "FrameAccessor::GetImage():" << "Failed to call renderRoI on input at frame" << frame << "with RoI x1="
                 << roi.x1 << "y1=" << roi.y1 << "x2=" << roi.x2 << "y2=" << roi.y2;
#endif

        return (mv::FrameAccessor::Key)0;
    }

    assert( !planes.empty() );
    const ImagePtr& sourceImage = planes.begin()->second;
    RectI sourceBounds = sourceImage->getBounds();
    RectI intersectedRoI;
    if ( !roi.intersect(sourceBounds, &intersectedRoI) ) {
#ifdef TRACE_LIB_MV
        qDebug() << QThread::currentThread() << "FrameAccessor::GetImage():" << "RoI does not intersect the source image bounds (RoI x1="
                 << roi.x1 << "y1=" << roi.y1 << "x2=" << roi.x2 << "y2=" << roi.y2 << ")";
#endif

        return (mv::FrameAccessor::Key)0;
    }

#ifdef TRACE_LIB_MV
    qDebug() << QThread::currentThread() << "FrameAccessor::GetImage():" << "renderRoi (frame" << frame << ") OK  (BOUNDS= x1="
             << sourceBounds.x1 << "y1=" << sourceBounds.y1 << "x2=" << sourceBounds.x2 << "y2=" << sourceBounds.y2 << ") (ROI = " << roi.x1 << "y1=" << roi.y1 << "x2=" << roi.x2 << "y2=" << roi.y2 << ")";
#endif

    /*
       Copy the Natron image to the LivMV float image
     */
    FrameAccessorCacheEntry entry;
    entry.image.reset( new MvFloatImage( intersectedRoI.height(), intersectedRoI.width() ) );
    entry.bounds = intersectedRoI;
    entry.referenceCount = 1;
    natronImageToLibMvFloatImage(_imp->enabledChannels,
                                 sourceImage.get(),
                                 intersectedRoI,
                                 *entry.image);
    // we ignore the transform parameter and do it in natronImageToLibMvFloatImage instead

    *destination = entry.image.get();
    //destination->CopyFrom<float>(*entry.image);

    //insert into the cache
    {
        QMutexLocker k(&_imp->cacheMutex);
        _imp->cache.insert( std::make_pair(key, entry) );
    }
#ifdef TRACE_LIB_MV
    qDebug() << QThread::currentThread() << "FrameAccessor::GetImage():" << "Rendered frame" << frame << "with RoI x1="
             << intersectedRoI.x1 << "y1=" << intersectedRoI.y1 << "x2=" << intersectedRoI.x2 << "y2=" << intersectedRoI.y2;
#endif

    return (mv::FrameAccessor::Key)entry.image.get();
} // TrackerFrameAccessor::GetImage