SurfaceLayerDetectionAlgorithm.cpp 3.64 KB
#include "SurfaceLayerDetectionAlgorithm.hpp"

#include "algorithm/common/PfcSegmenter.hpp"

#include "algorithm/gpu/common/MedianFilter.hpp"
#include "algorithm/gpu/common/MedianFilter3x3.hpp"

cv::cuda::GpuMat
SurfaceLayerDetectionAlgorithm::setMinimumTemperature(ushort temperature) {
	deviceMinTemperature.setTo(temperature);
	return deviceMinTemperature;
}

void SurfaceLayerDetectionAlgorithm::setup(const cv::Size &frameSize,
                                           const CVMatLoader &loader) {
	currentFrameSize = frameSize;
	medianFilter = createMedianFilter(3);

	deviceRegionMask.upload(loader.asMat("/scene_model/FOV", CV_8UC1) &
	                        PfcSegmenter(loader, frameSize).mask("Divertors"));

	medianFilterInput = std::make_unique<ContinuousGpuMat>(frameSize, CV_16UC1);
	medianFilterOutput =
	    std::make_unique<ContinuousGpuMat>(frameSize, CV_16UC1);

	deviceFrame = medianFilterInput->device();
	deviceFiltered = medianFilterOutput->device();

	deviceMinTemperature.create(frameSize, CV_16UC1);
	setMinimumTemperature(434);

	deviceFloatInput.create(frameSize, CV_32F);
	deviceDifference.create(frameSize, CV_32F);
	deviceDifference.setTo(cv::Scalar::all(0));

	deviceDifferenceAbs.create(frameSize, CV_32F);
	deviceDifferenceAbs.setTo(cv::Scalar::all(0));

	deviceSurfaceLayers.create(frameSize, CV_8UC1);
	deviceSurfaceLayers.setTo(cv::Scalar::all(0));
}

void SurfaceLayerDetectionAlgorithm::handleFrame(const cv::Mat &hostFrame,
                                                 unsigned long timestamp) {
	currentTimestamp = timestamp;
	deviceFrame.upload(hostFrame);

	/* Apply median filter */
	medianFilter->apply(deviceFrame, deviceFiltered);
	/* Apply detection PFC mask */
	cv::cuda::bitwise_and(deviceFiltered, deviceFiltered, deviceInput,
	                      deviceRegionMask);

	/* Trim noise at minimum temperature */
	cv::cuda::max(deviceInput, deviceMinTemperature, deviceInput);

	/* Convert to float */
	deviceInput.convertTo(deviceFloatInput, CV_32F);
	/* Calculate normalized derivative of the temperature evolution */
	normalizedDerivative(); /* Result in deviceDifference & deviceDifferenceAbs */

	/* Threshold rapidly evolving pixels */
	cv::cuda::compare(deviceDifferenceAbs, derivativeThreshold, deviceResult,
	                  cv::CMP_GE);
	/* Merge surface layers */
	cv::cuda::bitwise_or(deviceResult, deviceSurfaceLayers,
	                     deviceSurfaceLayers);
}

void SurfaceLayerDetectionAlgorithm::normalizedDerivative() {
	if (devicePreviousPreviousFrame.empty()) {
		previousPreviousTimestamp = currentTimestamp;
		deviceFloatInput.copyTo(devicePreviousPreviousFrame);
		return;
	}
	if (devicePreviousFrame.empty()) {
		previousTimestamp = currentTimestamp;
		deviceFloatInput.copyTo(devicePreviousFrame);
		return;
	}

	cv::cuda::subtract(deviceFloatInput, devicePreviousPreviousFrame,
	                   deviceDifference);
	cv::cuda::multiply(
	    deviceDifference,
	    cv::Scalar::all((currentTimestamp - previousPreviousTimestamp) *
	                    NS_TO_S),
	    deviceDifference);
	cv::cuda::divide(deviceDifference, devicePreviousFrame, deviceDifference);
	cv::cuda::abs(deviceDifference, deviceDifferenceAbs);

	previousPreviousTimestamp = previousTimestamp;
	previousTimestamp = currentTimestamp;

	devicePreviousFrame.copyTo(devicePreviousPreviousFrame);
	deviceFloatInput.copyTo(devicePreviousFrame);
}

std::unique_ptr<cv::cuda::Filter>
SurfaceLayerDetectionAlgorithm::createMedianFilter(int kernel) const {
	if (kernel == 3) {
		/* Implementation optimised for the small kernel size */
		return std::make_unique<MedianFilter3x3<ushort>>(currentFrameSize);
	}
	return CudaFilter::createMedianFilter16U(currentFrameSize, kernel);
}