marCCD frames are widened on the device
The marCCD reader read each frame's 16-bit scanlines into scratch and widened them to int32 on the worker thread (marccd::ReadInto, ~20 % of the image-loop worker CPU on 4nwv), and the 4-byte image was uploaded. ReadRawImage now reads the scanlines straight into the image's buffer and hands them over as a new read-only CompressionAlgorithm::UINT16_WIDENED (mode Int32): two bytes a pixel cross PCIe, from page-locked memory, and the preprocessing kernel widens them itself. It is not CompressedImageMode::Uint16, which calls 65535 an error pixel; a marCCD's 65535 is its saturation marker. So the kernel compares each pixel with int32's error marker and the int32 saturation cap - the comparisons are made on the pixel's value, in int64, rather than in the storage type - and decides every pixel exactly as for the int32 image the host would have widened it to. The host widening (JFJochDecompressPtr) is the CPU path. Test: ImagePreprocessorGPU_StoredEncodingsMatchCPU, with a frame carrying 65535 at a saturation of 65535, against the CPU preprocessor on the host-widened image. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EizBKhTqrAYago9KJG3dA3
This commit is contained in:
@@ -21,8 +21,8 @@ __global__ void preprocess_kernel(
|
||||
const uint8_t *__restrict__ mask,
|
||||
int32_t *__restrict__ output,
|
||||
ImageStatistics *__restrict__ stats,
|
||||
T saturation_limit,
|
||||
T err_value,
|
||||
int64_t saturation_limit,
|
||||
int64_t err_value,
|
||||
int npixels) {
|
||||
// Shared block accumulators
|
||||
__shared__ unsigned long long s_masked;
|
||||
@@ -410,7 +410,8 @@ ImageStatistics ImagePreprocessorGPU::Analyze(ImagePreprocessorBuffer &processed
|
||||
bool ImagePreprocessorGPU::AnalyzeCompressed(ImagePreprocessorBuffer &processed_image,
|
||||
const CompressedImage &image,
|
||||
ImageStatistics &stats) {
|
||||
if (image.GetCompressionAlgorithm() == CompressionAlgorithm::CBF_BYTE_OFFSET) {
|
||||
if (image.GetCompressionAlgorithm() == CompressionAlgorithm::CBF_BYTE_OFFSET
|
||||
|| image.GetCompressionAlgorithm() == CompressionAlgorithm::UINT16_WIDENED) {
|
||||
stats = AnalyzeStoredInt32(processed_image, image);
|
||||
return true;
|
||||
}
|
||||
@@ -543,6 +544,19 @@ ImageStatistics ImagePreprocessorGPU::AnalyzeStoredInt32(ImagePreprocessorBuffer
|
||||
if (!gpu_decompressed_image.get())
|
||||
gpu_decompressed_image = CudaDevicePtr<uint8_t>(npixels * sizeof(uint32_t));
|
||||
|
||||
if (image.GetCompressionAlgorithm() == CompressionAlgorithm::UINT16_WIDENED) {
|
||||
// Uploaded as stored, two bytes a pixel, and widened by the preprocessing kernel itself: with
|
||||
// int32's error marker and int32's saturation, each pixel is decided exactly as the int32 image
|
||||
// the host would have widened it to.
|
||||
last_decode = LastDecode::None;
|
||||
if (image.GetCompressedSize() != npixels * sizeof(uint16_t))
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Mismatch in size");
|
||||
PinInputRegion(image.GetCompressed(), image.GetCompressedSize());
|
||||
cuda_err(cudaMemcpyAsync(gpu_decompressed_image, image.GetCompressed(), image.GetCompressedSize(),
|
||||
cudaMemcpyHostToDevice, *stream));
|
||||
return AnalyzeOnDevice<uint16_t>(processed_image, INT32_MIN, INT32_MAX);
|
||||
}
|
||||
|
||||
last_decode = LastDecode::CBF;
|
||||
if (!cbf_decoder)
|
||||
cbf_decoder = std::make_unique<CBFDecoderGPU>(stream);
|
||||
@@ -556,9 +570,9 @@ ImageStatistics ImagePreprocessorGPU::AnalyzeStoredInt32(ImagePreprocessorBuffer
|
||||
// entry points so the two cannot drift apart.
|
||||
template<class T>
|
||||
ImageStatistics ImagePreprocessorGPU::AnalyzeOnDevice(ImagePreprocessorBuffer &processed_image,
|
||||
T err_value, T sat_value) {
|
||||
int64_t err_value, int64_t sat_value) {
|
||||
if (sat_value > saturation_limit)
|
||||
sat_value = static_cast<T>(saturation_limit);
|
||||
sat_value = saturation_limit;
|
||||
|
||||
cpu_stats[0] = ImageStatistics{.max_value = INT64_MIN, .min_value = INT64_MAX};
|
||||
cuda_err(cudaMemcpyAsync(gpu_stats, cpu_stats.data(), sizeof(ImageStatistics), cudaMemcpyHostToDevice, *stream));
|
||||
|
||||
Reference in New Issue
Block a user