Hello.
salt-and-pepper noise effect occurs when decoding certain Progressive Jpeg images.
Is there a way to solve this issue?
The version tested is cuda 12.4.
Sample code is enclosed.
#include <iostream>
#include <fstream>
#include <vector>
#include <cstdlib>
#include <nvjpeg.h>
#include <cuda_runtime.h>
// Error handling macros
#define CHECK_CUDA(call) \
{ \
const cudaError_t error = call; \
if (error != cudaSuccess) { \
std::cerr << "CUDA Error: " << __FILE__ << ":" << __LINE__ << ", " \
<< cudaGetErrorString(error) << std::endl; \
exit(1); \
} \
}
#define CHECK_NVJPEG(call) \
{ \
const nvjpegStatus_t status = call; \
if (status != NVJPEG_STATUS_SUCCESS) { \
std::cerr << "NVJPEG Error: " << __FILE__ << ":" << __LINE__ << ", code: " \
<< status << std::endl; \
exit(1); \
} \
}
void EncodeJPEG(unsigned char **ycbcr, int width, int height, const std::string &output_filename) {
nvjpegHandle_t nvjpeg_handle;
nvjpegEncoderState_t nvjpeg_state;
nvjpegEncoderParams_t nvjpeg_params;
nvjpegImage_t nvjpeg_image;
CHECK_NVJPEG(nvjpegCreateSimple(&nvjpeg_handle));
CHECK_NVJPEG(nvjpegEncoderStateCreate(nvjpeg_handle, &nvjpeg_state, NULL));
CHECK_NVJPEG(nvjpegEncoderParamsCreate(nvjpeg_handle, &nvjpeg_params, NULL));
nvjpeg_image.channel[0] = ycbcr[0];
nvjpeg_image.channel[1] = ycbcr[1];
nvjpeg_image.channel[2] = ycbcr[2];
nvjpeg_image.pitch[0] = width;
nvjpeg_image.pitch[1] = width / 2;
nvjpeg_image.pitch[2] = width / 2;
CHECK_NVJPEG(nvjpegEncoderParamsSetSamplingFactors(nvjpeg_params, NVJPEG_CSS_420, NULL));
CHECK_NVJPEG(nvjpegEncodeYUV(nvjpeg_handle,
nvjpeg_state,
nvjpeg_params,
&nvjpeg_image,
NVJPEG_CSS_420,
width,
height,
NULL));
// Retrieve encoded JPEG
std::vector<unsigned char> jpeg_buffer;
size_t jpeg_size;
CHECK_NVJPEG(nvjpegEncodeRetrieveBitstream(nvjpeg_handle, nvjpeg_state, NULL, &jpeg_size, NULL));
jpeg_buffer.resize(jpeg_size);
CHECK_NVJPEG(nvjpegEncodeRetrieveBitstream(nvjpeg_handle, nvjpeg_state, jpeg_buffer.data(), &jpeg_size, NULL));
// Save JPEG to file
std::ofstream output_file(output_filename.c_str(), std::ios::binary);
output_file.write(reinterpret_cast<char *>(jpeg_buffer.data()), jpeg_size);
output_file.close();
// Clean up
nvjpegEncoderStateDestroy(nvjpeg_state);
nvjpegEncoderParamsDestroy(nvjpeg_params);
nvjpegDestroy(nvjpeg_handle);
}
void DecodeJPEG(const std::string &input_filename, unsigned char **ycbcr, int &width, int &height) {
nvjpegHandle_t nvjpeg_handle;
nvjpegJpegState_t jpeg_state;
CHECK_NVJPEG(nvjpegCreateSimple(&nvjpeg_handle));
CHECK_NVJPEG(nvjpegJpegStateCreate(nvjpeg_handle, &jpeg_state));
// Load JPEG image into memory
std::ifstream file(input_filename.c_str(), std::ios::binary);
if (!file) {
std::cerr << "Cannot open file: " << input_filename.c_str() << std::endl;
exit(1);
}
file.seekg(0, std::ios::end);
size_t file_size = file.tellg();
file.seekg(0, std::ios::beg);
std::vector<unsigned char> jpeg_data(file_size);
file.read(reinterpret_cast<char *>(jpeg_data.data()), file_size);
file.close();
// Decode JPEG image
int nComponents;
nvjpegChromaSubsampling_t subsampling;
int widths[NVJPEG_MAX_COMPONENT];
int heights[NVJPEG_MAX_COMPONENT];
CHECK_NVJPEG(nvjpegGetImageInfo(nvjpeg_handle,
jpeg_data.data(),
jpeg_data.size(),
&nComponents,
&subsampling,
widths,
heights));
width = widths[0];
height = heights[0];
nvjpegImage_t nvjpeg_image;
for (int i = 0; i < nComponents; i++) {
CHECK_CUDA(cudaMalloc(&ycbcr[i], widths[i] * heights[i]));
cudaMemset(ycbcr[i], 0, widths[i] * heights[i]);
nvjpeg_image.channel[i] = ycbcr[i];
nvjpeg_image.pitch[i] = widths[i];
}
CHECK_NVJPEG(nvjpegDecode(nvjpeg_handle,
jpeg_state,
jpeg_data.data(),
jpeg_data.size(),
NVJPEG_OUTPUT_YUV,
&nvjpeg_image,
0));
// Cleanup
nvjpegJpegStateDestroy(jpeg_state);
nvjpegDestroy(nvjpeg_handle);
}
int main() {
std::vector<std::string> input_filenames;
input_filenames.push_back("papernoise_1080x1080_yuvj420p_progressive.jpg");
input_filenames.push_back("papernoise_1264x843_yuvj420p_progressive.jpg");
input_filenames.push_back("papernoise_1080x1350_yuvj420p_progressive.jpg");
std::vector<std::string> output_filenames;
output_filenames.push_back("papernoise_1080x1080_yuvj420p_progressive_out.jpg");
output_filenames.push_back("papernoise_1264x843_yuvj420p_progressive_out.jpg");
output_filenames.push_back("papernoise_1080x1350_yuvj420p_progressive_out.jpg");
unsigned char *ycbcr[3];
int width, height;
for (int i = 0; i < input_filenames.size(); i++) {
// Decode the progressive JPEG
DecodeJPEG(input_filenames[i], ycbcr, width, height);
// Encode the decoded image back to JPEG
EncodeJPEG(ycbcr, width, height, output_filenames[i]);
// Clean up
for (int i = 0; i < 3; i++) {
CHECK_CUDA(cudaFree(ycbcr[i]));
}
}
return 0;
}
jpeg_to_jpeg.cpp
export PATH=/usr/local/cuda/bin:$PATH
g++ -o jpeg_to_jpeg jpeg_to_jpeg.cpp -I/usr/local/cuda-12.3/include -L/usr/local/cuda-12.3/lib64 -lgif -lnvjpeg -lnppicc -lnppig -lnppc -lcuda -lcudart
./jpeg_to_jpeg
execute.sh
3 sample images