satdump/src-core/projection/warp/warp.cpp

497 lines
26 KiB
C++
Raw Permalink Normal View History

#include "warp_bkd.h"
2022-04-21 13:18:43 +02:00
#include "logger.h"
2024-03-14 12:12:34 +01:00
#include "core/exception.h"
2022-04-21 13:18:43 +02:00
#include <map>
2025-05-27 18:42:18 +01:00
#include "utils/binary.h"
#include "core/resources.h"
2022-09-12 02:23:48 +02:00
#include "core/opencl.h"
2023-02-26 00:54:58 +00:00
#include <chrono>
2023-05-08 23:08:34 +02:00
#include <cmath>
2025-05-26 19:21:10 +01:00
#include "utils/stats.h"
2022-04-21 13:18:43 +02:00
#include "common/geodetic/geodetic_coordinates.h"
2022-04-21 13:18:43 +02:00
namespace satdump
{
namespace warp
{
double lon_shift(double lon, double shift)
{
if (shift == 0)
return lon;
lon += shift;
if (lon > 180)
lon -= 360;
if (lon < -180)
lon += 360;
return lon;
}
void shift_latlon_by_lat(double *lat, double *lon, double shift)
{
if (shift == 0)
return;
double x = cos(*lat * DEG_TO_RAD) * cos(*lon * DEG_TO_RAD);
double y = cos(*lat * DEG_TO_RAD) * sin(*lon * DEG_TO_RAD);
double z = sin(*lat * DEG_TO_RAD);
double theta = shift * DEG_TO_RAD;
double x2 = x * cos(theta) + z * sin(theta);
double y2 = y;
double z2 = z * cos(theta) - x * sin(theta);
*lon = atan2(y2, x2) * RAD_TO_DEG;
double hyp = sqrt(x2 * x2 + y2 * y2);
*lat = atan2(z2, hyp) * RAD_TO_DEG;
}
2025-05-27 18:42:18 +01:00
std::shared_ptr<projection::VizGeorefSpline2D> initTPSTransform(WarpOperation &op)
2022-04-21 13:18:43 +02:00
{
return initTPSTransform(op.ground_control_points, op.shift_lon, op.shift_lat);
}
2022-04-21 13:18:43 +02:00
2025-05-27 18:42:18 +01:00
std::shared_ptr<projection::VizGeorefSpline2D> initTPSTransform(std::vector<projection::GCP> gcps, int shift_lon, int shift_lat)
{
2025-05-27 18:42:18 +01:00
std::shared_ptr<projection::VizGeorefSpline2D> spline_transform = std::make_shared<projection::VizGeorefSpline2D>(2);
2022-04-21 13:18:43 +02:00
// Attach (non-redundant) points to the transformation.
std::map<std::pair<double, double>, int> oMapPixelLineToIdx;
std::map<std::pair<double, double>, int> oMapXYToIdx;
for (int iGCP = 0; iGCP < (int)gcps.size(); iGCP++)
{
double final_lon = lon_shift(gcps[iGCP].lon, shift_lon);
double final_lat = gcps[iGCP].lat;
shift_latlon_by_lat(&final_lat, &final_lon, shift_lat);
2022-04-21 13:18:43 +02:00
const double afPL[2] = {gcps[iGCP].x, gcps[iGCP].y};
const double afXY[2] = {final_lon, final_lat};
2022-04-21 13:18:43 +02:00
std::map<std::pair<double, double>, int>::iterator oIter(oMapPixelLineToIdx.find(std::pair<double, double>(afPL[0], afPL[1])));
if (oIter != oMapPixelLineToIdx.end())
{
if (afXY[0] == gcps[oIter->second].lon && afXY[1] == gcps[oIter->second].lat)
continue;
else
{
2022-04-21 13:18:43 +02:00
logger->warn("2 GCPs have the same X,Y!");
continue;
}
2022-04-21 13:18:43 +02:00
}
else
oMapPixelLineToIdx[std::pair<double, double>(afPL[0], afPL[1])] = iGCP;
if (oMapXYToIdx.find(std::pair<double, double>(afXY[0], afXY[1])) != oMapXYToIdx.end())
{
2022-04-21 13:18:43 +02:00
logger->warn("2 GCPs have the same Lat,Lon!");
continue;
}
2022-04-21 13:18:43 +02:00
else
oMapXYToIdx[std::pair<double, double>(afXY[0], afXY[1])] = iGCP;
if (!spline_transform->add_point(afXY[0], afXY[1], afPL))
{
logger->error("Error generating transformer!");
// return 1;
}
}
2023-05-08 23:08:34 +02:00
logger->info("Solving TPS equations for %d GCPs...", gcps.size());
auto solve_start = std::chrono::system_clock::now();
2022-04-21 13:18:43 +02:00
bool solved = spline_transform->solve() != 0;
if (solved)
2023-05-08 23:08:34 +02:00
logger->info("Solved! Took %f", (std::chrono::system_clock::now() - solve_start).count() / 1e9);
2022-04-21 13:18:43 +02:00
else
logger->error("Failure solving!");
return spline_transform;
}
WarpCropSettings choseCropArea(WarpOperation &op)
{
WarpCropSettings cset;
cset.lat_min = -90;
cset.lat_max = 90;
cset.lon_min = -180;
cset.lon_max = 180;
cset.y_min = 0;
cset.y_max = op.output_height;
cset.x_min = 0;
cset.x_max = op.output_width;
std::vector<double> lat_values;
std::vector<double> lon_values;
2025-05-27 18:42:18 +01:00
for (projection::GCP &g : op.ground_control_points)
2022-04-21 13:18:43 +02:00
{
lat_values.push_back(g.lat);
lon_values.push_back(g.lon);
}
double lat_min = 0;
double lat_max = 0;
double lon_min = 0;
double lon_max = 0;
lat_min = lat_max = avg_overflowless(lat_values);
lon_min = lon_max = avg_overflowless(lon_values);
2025-05-27 18:42:18 +01:00
for (projection::GCP &g : op.ground_control_points)
2022-04-21 13:18:43 +02:00
{
if (g.lat > lat_max)
lat_max = g.lat;
if (g.lat < lat_min)
lat_min = g.lat;
if (g.lon > lon_max)
lon_max = g.lon;
if (g.lon < lon_min)
lon_min = g.lon;
}
2022-04-22 18:45:40 +02:00
// Round to integer degrees
2022-04-21 13:18:43 +02:00
cset.lat_min = floor(lat_min);
cset.lon_min = floor(lon_min);
cset.lat_max = ceil(lat_max);
cset.lon_max = ceil(lon_max);
2023-10-06 12:55:58 +02:00
if (op.shift_lat == 90)
cset.lat_max = 90;
if (op.shift_lat == -90)
cset.lat_min = -90;
2022-04-22 18:45:40 +02:00
// Compute to pixels
2022-04-21 13:18:43 +02:00
cset.y_max = op.output_height - ((90.0f + cset.lat_min) / 180.0f) * op.output_height;
cset.y_min = op.output_height - ((90.0f + cset.lat_max) / 180.0f) * op.output_height;
cset.x_min = (cset.lon_min / 360.0f) * op.output_width + (op.output_width / 2);
cset.x_max = (cset.lon_max / 360.0f) * op.output_width + (op.output_width / 2);
2022-04-22 18:45:40 +02:00
// Pixels can offset it a bit - recompute to be 100% accurate
cset.lat_max = ((op.output_height - cset.y_min) / (double)op.output_height) * 180.0f - 90.0f;
cset.lat_min = ((op.output_height - cset.y_max) / (double)op.output_height) * 180.0f - 90.0f;
cset.lon_min = (cset.x_min / (double)op.output_width) * 360.0f - 180.0f;
cset.lon_max = (cset.x_max / (double)op.output_width) * 360.0f - 180.0f;
2022-04-21 13:18:43 +02:00
return cset;
}
2022-04-22 14:19:29 +02:00
void ImageWarper::warpOnCPU(WarpResult &result)
2022-04-21 13:18:43 +02:00
{
2022-04-22 14:19:29 +02:00
// Now, warp the image
2022-04-21 19:44:17 +02:00
auto cpu_start = std::chrono::system_clock::now();
2022-04-21 13:18:43 +02:00
{
2022-09-29 00:46:17 +02:00
#pragma omp parallel for
for (int64_t xy_ptr = 0; xy_ptr < (int64_t)result.output_image.width() * (int64_t)result.output_image.height(); xy_ptr++)
2022-04-21 13:18:43 +02:00
{
2022-09-29 00:46:17 +02:00
double xx, yy;
double xy[2];
2022-04-21 13:18:43 +02:00
int x = (xy_ptr % result.output_image.width());
int y = (xy_ptr / result.output_image.width());
// Scale to the map
double lat = -((double)(y + crop_set.y_min) / (double)op.output_height) * 180 + 90;
double lon = ((double)(x + crop_set.x_min) / (double)op.output_width) * 360 - 180;
// Perform TPS
shift_latlon_by_lat(&lat, &lon, op.shift_lat);
tps->get_point(lon_shift(lon, op.shift_lon), lat, xy);
2022-04-21 13:18:43 +02:00
xx = xy[0];
yy = xy[1];
if (xx < 0 || yy < 0)
continue;
2024-11-18 16:40:57 -05:00
if ((int)xx > (int)op.input_image->width() - 1 || (int)yy > (int)op.input_image->height() - 1)
2022-04-21 13:18:43 +02:00
continue;
2022-09-23 20:46:41 +02:00
if (result.output_image.channels() == 4)
{
2024-11-18 16:40:57 -05:00
if (op.input_image->channels() == 1)
2022-09-23 20:46:41 +02:00
for (int c = 0; c < 3; c++)
2024-11-18 16:40:57 -05:00
result.output_image.set(c, y * result.output_image.width() + x, op.input_image->get_pixel_bilinear(0, xx, yy));
else if (op.input_image->channels() == 3 || op.input_image->channels() == 4)
2022-09-23 20:46:41 +02:00
for (int c = 0; c < 3; c++)
2024-11-18 16:40:57 -05:00
result.output_image.set(c, y * result.output_image.width() + x, op.input_image->get_pixel_bilinear(c, xx, yy));
2024-03-31 16:47:14 +02:00
2024-11-18 16:40:57 -05:00
if (op.input_image->channels() == 4)
result.output_image.set(3, y * result.output_image.width() + x, op.input_image->get_pixel_bilinear(3, xx, yy));
2024-03-31 16:47:14 +02:00
else
2024-05-08 20:17:42 +02:00
result.output_image.set(3, y * result.output_image.width() + x, 65535);
2022-09-23 20:46:41 +02:00
}
else
{
2024-11-18 16:40:57 -05:00
for (int c = 0; c < op.input_image->channels(); c++)
result.output_image.set(c, y * result.output_image.width() + x, op.input_image->get_pixel_bilinear(c, xx, yy));
2022-09-23 20:46:41 +02:00
}
2022-04-21 13:18:43 +02:00
}
}
2022-04-21 19:44:17 +02:00
auto cpu_time = (std::chrono::system_clock::now() - cpu_start);
2023-05-08 23:08:34 +02:00
logger->debug("CPU Processing Time %f", cpu_time.count() / 1e9);
2022-04-21 13:18:43 +02:00
}
#ifdef USE_OPENCL
2022-08-30 12:01:12 +02:00
void ImageWarper::warpOnGPU_fp64(WarpResult &result)
2022-04-21 13:18:43 +02:00
{
// Build GPU Kernel
2022-09-01 14:01:33 +02:00
cl_program warping_program = opencl::buildCLKernel(resources::getResourcePath("opencl/warp_image_thin_plate_spline_fp64.cl"));
2022-08-30 12:01:12 +02:00
2022-09-01 14:01:33 +02:00
cl_int err = 0;
2022-08-30 12:01:12 +02:00
auto &context = satdump::opencl::ocl_context;
auto &device = satdump::opencl::ocl_device;
2022-04-21 13:18:43 +02:00
// Now, run the actual OpenCL Kernel
2022-04-21 19:44:17 +02:00
auto gpu_start = std::chrono::system_clock::now();
2022-04-21 13:18:43 +02:00
{
// Images
2022-09-01 14:01:33 +02:00
cl_mem buffer_map = clCreateBuffer(context, CL_MEM_READ_WRITE, sizeof(uint16_t) * result.output_image.size(), NULL, &err);
if (err != CL_SUCCESS)
2024-08-03 09:56:34 -04:00
throw satdump_exception("Couldn't load buffer_map! Code " + std::to_string(err));
2024-11-18 16:40:57 -05:00
cl_mem buffer_img = clCreateBuffer(context, CL_MEM_READ_WRITE, sizeof(uint16_t) * op.input_image->size(), NULL, &err);
2022-09-01 14:01:33 +02:00
if (err != CL_SUCCESS)
2024-08-03 09:56:34 -04:00
throw satdump_exception("Couldn't load buffer_img! Code " + std::to_string(err));
2022-04-21 13:18:43 +02:00
// TPS Stuff
2022-09-01 14:01:33 +02:00
cl_mem buffer_tps_npoints = clCreateBuffer(context, CL_MEM_READ_WRITE, sizeof(int), NULL, &err);
cl_mem buffer_tps_x = clCreateBuffer(context, CL_MEM_READ_WRITE, sizeof(double) * tps->_nof_points, NULL, &err);
cl_mem buffer_tps_y = clCreateBuffer(context, CL_MEM_READ_WRITE, sizeof(double) * tps->_nof_points, NULL, &err);
cl_mem buffer_tps_coefs1 = clCreateBuffer(context, CL_MEM_READ_WRITE, sizeof(double) * tps->_nof_eqs, NULL, &err);
cl_mem buffer_tps_coefs2 = clCreateBuffer(context, CL_MEM_READ_WRITE, sizeof(double) * tps->_nof_eqs, NULL, &err);
cl_mem buffer_tps_xmean = clCreateBuffer(context, CL_MEM_READ_WRITE, sizeof(double), NULL, &err);
cl_mem buffer_tps_ymean = clCreateBuffer(context, CL_MEM_READ_WRITE, sizeof(double), NULL, &err);
2022-04-21 13:18:43 +02:00
int img_settings[] = {op.output_width, op.output_height,
2024-11-18 16:40:57 -05:00
(int)op.input_image->width(), (int)op.input_image->height(),
op.input_image->channels(),
2022-09-23 20:46:41 +02:00
result.output_image.channels(),
2022-04-21 13:18:43 +02:00
crop_set.y_min, crop_set.y_max,
crop_set.x_min, crop_set.x_max,
op.shift_lon, op.shift_lat};
2022-04-21 13:18:43 +02:00
cl_mem buffer_img_settings = clCreateBuffer(context, CL_MEM_READ_WRITE, sizeof(int) * 12, NULL, &err);
2022-04-21 13:18:43 +02:00
// Create an OpenCL queue
2022-09-01 14:01:33 +02:00
cl_command_queue queue = clCreateCommandQueue(context, device, 0, &err);
2024-08-03 09:56:34 -04:00
if (err != CL_SUCCESS)
throw satdump_exception("Couldn't create OpenCL queue! Code " + std::to_string(err));
2022-04-21 13:18:43 +02:00
// Write all of buffers to the GPU
2024-05-08 20:17:42 +02:00
clEnqueueWriteBuffer(queue, buffer_map, true, 0, sizeof(uint16_t) * result.output_image.size(), result.output_image.raw_data(), 0, NULL, NULL);
2024-11-18 16:40:57 -05:00
clEnqueueWriteBuffer(queue, buffer_img, true, 0, sizeof(uint16_t) * op.input_image->size(), op.input_image->raw_data(), 0, NULL, NULL);
2022-09-01 14:01:33 +02:00
clEnqueueWriteBuffer(queue, buffer_tps_npoints, true, 0, sizeof(int), &tps->_nof_points, 0, NULL, NULL);
clEnqueueWriteBuffer(queue, buffer_tps_x, true, 0, sizeof(double) * tps->_nof_points, tps->x, 0, NULL, NULL);
clEnqueueWriteBuffer(queue, buffer_tps_y, true, 0, sizeof(double) * tps->_nof_points, tps->y, 0, NULL, NULL);
clEnqueueWriteBuffer(queue, buffer_tps_coefs1, true, 0, sizeof(double) * tps->_nof_eqs, tps->coef[0], 0, NULL, NULL);
clEnqueueWriteBuffer(queue, buffer_tps_coefs2, true, 0, sizeof(double) * tps->_nof_eqs, tps->coef[1], 0, NULL, NULL);
clEnqueueWriteBuffer(queue, buffer_tps_xmean, true, 0, sizeof(double), &tps->x_mean, 0, NULL, NULL);
clEnqueueWriteBuffer(queue, buffer_tps_ymean, true, 0, sizeof(double), &tps->y_mean, 0, NULL, NULL);
clEnqueueWriteBuffer(queue, buffer_img_settings, true, 0, sizeof(int) * 12, img_settings, 0, NULL, NULL);
2022-04-21 13:18:43 +02:00
// Init the kernel
2022-09-01 14:01:33 +02:00
cl_kernel warping_kernel = clCreateKernel(warping_program, "warp_image_thin_plate_spline", &err);
clSetKernelArg(warping_kernel, 0, sizeof(cl_mem), &buffer_map);
clSetKernelArg(warping_kernel, 1, sizeof(cl_mem), &buffer_img);
clSetKernelArg(warping_kernel, 2, sizeof(cl_mem), &buffer_tps_npoints);
clSetKernelArg(warping_kernel, 3, sizeof(cl_mem), &buffer_tps_x);
clSetKernelArg(warping_kernel, 4, sizeof(cl_mem), &buffer_tps_y);
clSetKernelArg(warping_kernel, 5, sizeof(cl_mem), &buffer_tps_coefs1);
clSetKernelArg(warping_kernel, 6, sizeof(cl_mem), &buffer_tps_coefs2);
clSetKernelArg(warping_kernel, 7, sizeof(cl_mem), &buffer_tps_xmean);
clSetKernelArg(warping_kernel, 8, sizeof(cl_mem), &buffer_tps_ymean);
clSetKernelArg(warping_kernel, 9, sizeof(cl_mem), &buffer_img_settings);
2022-04-21 13:18:43 +02:00
// Get proper workload size
2022-09-01 14:01:33 +02:00
size_t size_wg = 0;
size_t compute_units = 0;
clGetDeviceInfo(device, CL_DEVICE_MAX_WORK_GROUP_SIZE, sizeof(size_t), &size_wg, NULL);
clGetDeviceInfo(device, CL_DEVICE_MAX_COMPUTE_UNITS, sizeof(size_t), &compute_units, NULL);
2022-04-21 13:18:43 +02:00
2023-05-08 23:08:34 +02:00
logger->debug("Workgroup size %d", size_wg * compute_units);
2022-04-21 13:18:43 +02:00
// Run the kernel!
2022-09-01 14:01:33 +02:00
size_t total_wg_size = int(size_wg) * int(compute_units);
2024-08-03 09:56:34 -04:00
err = clEnqueueNDRangeKernel(queue, warping_kernel, 1, NULL, &total_wg_size, NULL, 0, NULL, NULL);
if (err != CL_SUCCESS)
throw satdump_exception("Couldn't clEnqueueNDRangeKernel! Code " + std::to_string(err));
2022-04-21 13:18:43 +02:00
// Read image result back from VRAM
2024-05-08 20:17:42 +02:00
clEnqueueReadBuffer(queue, buffer_map, true, 0, sizeof(uint16_t) * result.output_image.size(), result.output_image.raw_data(), 0, NULL, NULL);
2022-09-01 14:01:33 +02:00
// Free up everything
clReleaseMemObject(buffer_img);
clReleaseMemObject(buffer_map);
clReleaseMemObject(buffer_tps_npoints);
clReleaseMemObject(buffer_tps_x);
clReleaseMemObject(buffer_tps_y);
clReleaseMemObject(buffer_tps_coefs1);
clReleaseMemObject(buffer_tps_coefs2);
clReleaseMemObject(buffer_tps_xmean);
clReleaseMemObject(buffer_tps_ymean);
clReleaseMemObject(buffer_img_settings);
clReleaseKernel(warping_kernel);
2022-09-24 19:56:38 +02:00
// clReleaseProgram(warping_program);
2022-09-01 14:01:33 +02:00
clReleaseCommandQueue(queue);
2022-04-21 13:18:43 +02:00
}
2022-04-21 19:44:17 +02:00
auto gpu_time = (std::chrono::system_clock::now() - gpu_start);
2023-05-08 23:08:34 +02:00
logger->debug("GPU Processing Time %f", gpu_time.count() / 1e9);
2022-04-21 13:18:43 +02:00
}
2022-04-21 16:10:00 +02:00
2022-08-30 12:01:12 +02:00
void ImageWarper::warpOnGPU_fp32(WarpResult &result)
2022-04-21 16:10:00 +02:00
{
// Build GPU Kernel
2022-09-01 14:01:33 +02:00
cl_program warping_program = opencl::buildCLKernel(resources::getResourcePath("opencl/warp_image_thin_plate_spline_fp32.cl"));
2022-08-30 12:01:12 +02:00
2022-09-01 14:01:33 +02:00
cl_int err = 0;
2022-08-30 12:01:12 +02:00
auto &context = satdump::opencl::ocl_context;
auto &device = satdump::opencl::ocl_device;
2022-04-21 16:10:00 +02:00
// Now, run the actual OpenCL Kernel
2022-04-21 19:44:17 +02:00
auto gpu_start = std::chrono::system_clock::now();
2022-04-21 16:10:00 +02:00
{
// Images
2022-09-01 14:01:33 +02:00
cl_mem buffer_map = clCreateBuffer(context, CL_MEM_READ_WRITE, sizeof(uint16_t) * result.output_image.size(), NULL, &err);
if (err != CL_SUCCESS)
2024-08-03 09:56:34 -04:00
throw satdump_exception("Couldn't load buffer_map! Code " + std::to_string(err));
2024-11-18 16:40:57 -05:00
cl_mem buffer_img = clCreateBuffer(context, CL_MEM_READ_WRITE, sizeof(uint16_t) * op.input_image->size(), NULL, &err);
2022-09-01 14:01:33 +02:00
if (err != CL_SUCCESS)
2024-08-03 09:56:34 -04:00
throw satdump_exception("Couldn't load buffer_img! Code " + std::to_string(err));
2022-04-21 16:10:00 +02:00
// TPS Stuff
2022-09-01 14:01:33 +02:00
cl_mem buffer_tps_npoints = clCreateBuffer(context, CL_MEM_READ_WRITE, sizeof(float) * tps->_nof_points, NULL, &err);
cl_mem buffer_tps_x = clCreateBuffer(context, CL_MEM_READ_WRITE, sizeof(float) * tps->_nof_points, NULL, &err);
cl_mem buffer_tps_y = clCreateBuffer(context, CL_MEM_READ_WRITE, sizeof(float) * tps->_nof_points, NULL, &err);
cl_mem buffer_tps_coefs1 = clCreateBuffer(context, CL_MEM_READ_WRITE, sizeof(float) * tps->_nof_eqs, NULL, &err);
cl_mem buffer_tps_coefs2 = clCreateBuffer(context, CL_MEM_READ_WRITE, sizeof(float) * tps->_nof_eqs, NULL, &err);
cl_mem buffer_tps_xmean = clCreateBuffer(context, CL_MEM_READ_WRITE, sizeof(float), NULL, &err);
cl_mem buffer_tps_ymean = clCreateBuffer(context, CL_MEM_READ_WRITE, sizeof(float), NULL, &err);
2022-04-21 16:10:00 +02:00
int img_settings[] = {op.output_width, op.output_height,
2024-11-18 16:40:57 -05:00
(int)op.input_image->width(), (int)op.input_image->height(),
op.input_image->channels(),
2022-09-23 20:46:41 +02:00
result.output_image.channels(),
2022-04-21 16:10:00 +02:00
crop_set.y_min, crop_set.y_max,
crop_set.x_min, crop_set.x_max,
op.shift_lon, op.shift_lat};
2022-04-21 16:10:00 +02:00
cl_mem buffer_img_settings = clCreateBuffer(context, CL_MEM_READ_WRITE, sizeof(int) * 12, NULL, &err);
2022-04-21 16:10:00 +02:00
// Create an OpenCL queue
2022-09-01 14:01:33 +02:00
cl_command_queue queue = clCreateCommandQueue(context, device, 0, &err);
2024-08-03 09:56:34 -04:00
if (err != CL_SUCCESS)
throw satdump_exception("Couldn't create OpenCL queue! Code " + std::to_string(err));
2022-04-21 16:10:00 +02:00
// Write all of buffers to the GPU, also converting to FP32
2024-05-08 20:17:42 +02:00
clEnqueueWriteBuffer(queue, buffer_map, true, 0, sizeof(uint16_t) * result.output_image.size(), result.output_image.raw_data(), 0, NULL, NULL);
2024-11-18 16:40:57 -05:00
clEnqueueWriteBuffer(queue, buffer_img, true, 0, sizeof(uint16_t) * op.input_image->size(), op.input_image->raw_data(), 0, NULL, NULL);
2022-09-01 14:01:33 +02:00
clEnqueueWriteBuffer(queue, buffer_tps_npoints, true, 0, sizeof(int), &tps->_nof_points, 0, NULL, NULL);
2022-04-21 16:10:00 +02:00
std::vector<float> tps_x = double_buffer_to_float(tps->x, tps->_nof_points);
std::vector<float> tps_y = double_buffer_to_float(tps->y, tps->_nof_points);
std::vector<float> tps_coef1 = double_buffer_to_float(tps->coef[0], tps->_nof_eqs);
std::vector<float> tps_coef2 = double_buffer_to_float(tps->coef[1], tps->_nof_eqs);
float tps_x_mean = tps->x_mean;
float tps_y_mean = tps->y_mean;
2022-09-01 14:01:33 +02:00
clEnqueueWriteBuffer(queue, buffer_tps_x, true, 0, sizeof(float) * tps->_nof_points, tps_x.data(), 0, NULL, NULL);
clEnqueueWriteBuffer(queue, buffer_tps_y, true, 0, sizeof(float) * tps->_nof_points, tps_y.data(), 0, NULL, NULL);
clEnqueueWriteBuffer(queue, buffer_tps_coefs1, true, 0, sizeof(float) * tps->_nof_eqs, tps_coef1.data(), 0, NULL, NULL);
clEnqueueWriteBuffer(queue, buffer_tps_coefs2, true, 0, sizeof(float) * tps->_nof_eqs, tps_coef2.data(), 0, NULL, NULL);
clEnqueueWriteBuffer(queue, buffer_tps_xmean, true, 0, sizeof(float), &tps_x_mean, 0, NULL, NULL);
clEnqueueWriteBuffer(queue, buffer_tps_ymean, true, 0, sizeof(float), &tps_y_mean, 0, NULL, NULL);
clEnqueueWriteBuffer(queue, buffer_img_settings, true, 0, sizeof(int) * 12, img_settings, 0, NULL, NULL);
2022-04-21 16:10:00 +02:00
// Init the kernel
2022-09-01 14:01:33 +02:00
cl_kernel warping_kernel = clCreateKernel(warping_program, "warp_image_thin_plate_spline", &err);
clSetKernelArg(warping_kernel, 0, sizeof(cl_mem), &buffer_map);
clSetKernelArg(warping_kernel, 1, sizeof(cl_mem), &buffer_img);
clSetKernelArg(warping_kernel, 2, sizeof(cl_mem), &buffer_tps_npoints);
clSetKernelArg(warping_kernel, 3, sizeof(cl_mem), &buffer_tps_x);
clSetKernelArg(warping_kernel, 4, sizeof(cl_mem), &buffer_tps_y);
clSetKernelArg(warping_kernel, 5, sizeof(cl_mem), &buffer_tps_coefs1);
clSetKernelArg(warping_kernel, 6, sizeof(cl_mem), &buffer_tps_coefs2);
clSetKernelArg(warping_kernel, 7, sizeof(cl_mem), &buffer_tps_xmean);
clSetKernelArg(warping_kernel, 8, sizeof(cl_mem), &buffer_tps_ymean);
clSetKernelArg(warping_kernel, 9, sizeof(cl_mem), &buffer_img_settings);
2022-04-21 16:10:00 +02:00
// Get proper workload size
2022-09-01 14:01:33 +02:00
size_t size_wg = 0;
size_t compute_units = 0;
clGetDeviceInfo(device, CL_DEVICE_MAX_WORK_GROUP_SIZE, sizeof(size_t), &size_wg, NULL);
clGetDeviceInfo(device, CL_DEVICE_MAX_COMPUTE_UNITS, sizeof(size_t), &compute_units, NULL);
2022-04-21 16:10:00 +02:00
2023-05-08 23:08:34 +02:00
logger->debug("Workgroup size %d", size_wg * compute_units);
2022-04-21 16:10:00 +02:00
// Run the kernel!
2022-09-01 14:01:33 +02:00
size_t total_wg_size = int(size_wg) * int(compute_units);
2024-08-03 09:56:34 -04:00
err = clEnqueueNDRangeKernel(queue, warping_kernel, 1, NULL, &total_wg_size, NULL, 0, NULL, NULL);
if (err != CL_SUCCESS)
throw satdump_exception("Couldn't clEnqueueNDRangeKernel! Code " + std::to_string(err));
2022-04-21 16:10:00 +02:00
// Read image result back from VRAM
2024-05-08 20:17:42 +02:00
clEnqueueReadBuffer(queue, buffer_map, true, 0, sizeof(uint16_t) * result.output_image.size(), result.output_image.raw_data(), 0, NULL, NULL);
2022-09-01 14:01:33 +02:00
// Free up everything
clReleaseMemObject(buffer_img);
clReleaseMemObject(buffer_map);
clReleaseMemObject(buffer_tps_npoints);
clReleaseMemObject(buffer_tps_x);
clReleaseMemObject(buffer_tps_y);
clReleaseMemObject(buffer_tps_coefs1);
clReleaseMemObject(buffer_tps_coefs2);
clReleaseMemObject(buffer_tps_xmean);
clReleaseMemObject(buffer_tps_ymean);
clReleaseMemObject(buffer_img_settings);
clReleaseKernel(warping_kernel);
2022-09-24 19:56:38 +02:00
// clReleaseProgram(warping_program);
2022-09-01 14:01:33 +02:00
clReleaseCommandQueue(queue);
2022-04-21 16:10:00 +02:00
}
2022-04-21 19:44:17 +02:00
auto gpu_time = (std::chrono::system_clock::now() - gpu_start);
2023-05-08 23:08:34 +02:00
logger->debug("GPU Processing Time %f", gpu_time.count() / 1e9);
2022-04-21 16:10:00 +02:00
}
2022-04-21 13:18:43 +02:00
#endif
void ImageWarper::update(bool skip_tps)
2022-04-21 13:18:43 +02:00
{
if (!skip_tps)
tps = initTPSTransform(op);
2022-04-22 14:19:29 +02:00
crop_set = choseCropArea(op);
}
2023-10-04 00:43:33 +02:00
WarpResult ImageWarper::warp(bool force_double)
2022-04-22 14:19:29 +02:00
{
WarpResult result;
// Prepare the output
2024-05-09 01:16:08 +02:00
result.output_image = image::Image(16, // TODOIMG ALLOW 8-bits
2024-05-08 20:17:42 +02:00
crop_set.x_max - crop_set.x_min, crop_set.y_max - crop_set.y_min,
2024-11-18 16:40:57 -05:00
op.output_rgba ? 4 : op.input_image->channels());
2022-04-22 18:45:40 +02:00
result.top_left = {0, 0, (double)crop_set.lon_min, (double)crop_set.lat_max}; // 0,0
result.top_right = {(double)result.output_image.width() - 1, 0, (double)crop_set.lon_max, (double)crop_set.lat_max}; // 1,0
result.bottom_left = {0, (double)result.output_image.height() - 1, (double)crop_set.lon_min, (double)crop_set.lat_min}; // 0,1
result.bottom_right = {(double)result.output_image.width() - 1, (double)result.output_image.height() - 1, (double)crop_set.lon_max, (double)crop_set.lat_min}; // 1,1
2022-04-22 14:19:29 +02:00
2022-04-21 13:18:43 +02:00
#ifdef USE_OPENCL
if (satdump::opencl::useCL())
2022-04-21 13:18:43 +02:00
{
try
{
logger->debug("Using GPU! Double precision requested %d", (int)force_double);
satdump::opencl::setupOCLContext();
if (force_double)
warpOnGPU_fp64(result);
else
warpOnGPU_fp32(result);
return result;
}
catch (std::runtime_error &e)
{
logger->error("Error warping on GPU : %s", e.what());
}
2022-04-21 13:18:43 +02:00
}
#endif
logger->debug("Using CPU!");
2022-04-22 14:19:29 +02:00
warpOnCPU(result);
return result;
2022-04-21 13:18:43 +02:00
}
}
}