205 lines
10 KiB
C++
205 lines
10 KiB
C++
#include <lardon3d/acquisition_ingest.h>
|
|
|
|
#include <lardon3d/acquisition_pairing.h>
|
|
extern "C" {
|
|
#include <lardon3d/image_catalog.h>
|
|
}
|
|
#include <lardon3d/raw_development.h>
|
|
|
|
#include <climits>
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
|
|
namespace {
|
|
|
|
#ifdef LARDON3D_ACQUISITION_INGEST_TESTING
|
|
const Lardon3DAcquisitionMetadata *testing_metadata = nullptr;
|
|
size_t testing_metadata_count = 0u;
|
|
#endif
|
|
|
|
struct PublishedSource {
|
|
Lardon3DProjectDbImageAsset asset;
|
|
Lardon3DAcquisitionMetadata metadata;
|
|
char managed_path[PATH_MAX];
|
|
};
|
|
|
|
bool valid_options(const Lardon3DAcquisitionIngestOptions &options) {
|
|
return (options.grouping == LARDON3D_ACQUISITION_GROUP_AUTOMATIC ||
|
|
options.grouping == LARDON3D_ACQUISITION_GROUP_CALLER_EXPLICIT) &&
|
|
(options.representation == LARDON3D_ACQUISITION_SELECT_JPEG_SOURCE ||
|
|
options.representation == LARDON3D_ACQUISITION_SELECT_DEVELOP_RAW) &&
|
|
options.select_representation <= 1u && options.imported_at >= 0 &&
|
|
options.max_source_bytes != 0u;
|
|
}
|
|
|
|
const char *base_name(const char *path) {
|
|
const char *slash = std::strrchr(path, '/');
|
|
return slash == nullptr ? path : slash + 1;
|
|
}
|
|
|
|
Lardon3DAcquisitionIngestResult db_result(Lardon3DProjectDbResult result) {
|
|
if (result == LARDON3D_PROJECT_DB_CONSTRAINT) return LARDON3D_ACQUISITION_INGEST_CONSTRAINT;
|
|
return result == LARDON3D_PROJECT_DB_NOT_FOUND ? LARDON3D_ACQUISITION_INGEST_SCANSET_NOT_FOUND
|
|
: LARDON3D_ACQUISITION_INGEST_DB_ERROR;
|
|
}
|
|
|
|
bool add_to_group(Lardon3DAcquisitionIngestGroup &group, size_t source_index) {
|
|
if (group.source_count >= LARDON3D_ACQUISITION_INGEST_MAX_SOURCES) return false;
|
|
group.source_indices[group.source_count++] = source_index;
|
|
return true;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
#ifdef LARDON3D_ACQUISITION_INGEST_TESTING
|
|
extern "C" void lardon3d_acquisition_ingest_test_set_metadata(
|
|
const Lardon3DAcquisitionMetadata *metadata, size_t count) {
|
|
testing_metadata = metadata;
|
|
testing_metadata_count = count;
|
|
}
|
|
#endif
|
|
|
|
extern "C" Lardon3DAcquisitionIngestResult lardon3d_acquisition_ingest(
|
|
Lardon3DAppState *state, uint64_t scanset_id,
|
|
const Lardon3DAcquisitionIngestSource *sources, size_t source_count,
|
|
const Lardon3DAcquisitionIngestOptions *options, Lardon3DAcquisitionIngestOutput *output) {
|
|
if (output != nullptr) std::memset(output, 0, sizeof(*output));
|
|
if (state == nullptr || !state->project_loaded || state->project_db == nullptr || scanset_id == 0u ||
|
|
sources == nullptr || source_count == 0u ||
|
|
source_count > LARDON3D_ACQUISITION_INGEST_MAX_SOURCES || options == nullptr ||
|
|
!valid_options(*options) || output == nullptr) return LARDON3D_ACQUISITION_INGEST_INVALID_ARGUMENT;
|
|
Lardon3DProjectDbScanSet scanset{};
|
|
Lardon3DProjectDbResult database_result =
|
|
lardon3d_project_db_load_scanset(state->project_db, scanset_id, &scanset);
|
|
if (database_result != LARDON3D_PROJECT_DB_OK) return db_result(database_result);
|
|
if (options->grouping == LARDON3D_ACQUISITION_GROUP_CALLER_EXPLICIT) {
|
|
for (size_t i = 0; i < source_count; ++i)
|
|
if (sources[i].explicit_group == 0u) return LARDON3D_ACQUISITION_INGEST_INVALID_ARGUMENT;
|
|
}
|
|
|
|
PublishedSource published[LARDON3D_ACQUISITION_INGEST_MAX_SOURCES]{};
|
|
for (size_t i = 0; i < source_count; ++i) {
|
|
if (sources[i].source_path == nullptr || sources[i].source_path[0] == '\0')
|
|
return LARDON3D_ACQUISITION_INGEST_INVALID_ARGUMENT;
|
|
if (lardon3d_image_catalog_publish_asset_file(state, sources[i].source_path,
|
|
options->imported_at, options->max_source_bytes, &published[i].asset) !=
|
|
LARDON3D_IMAGE_CATALOG_ASSET_PUBLISHED) return LARDON3D_ACQUISITION_INGEST_PUBLISH_ERROR;
|
|
for (size_t previous = 0; previous < i; ++previous)
|
|
if (published[previous].asset.asset_id == published[i].asset.asset_id)
|
|
return LARDON3D_ACQUISITION_INGEST_DUPLICATE_ASSET;
|
|
int length = std::snprintf(published[i].managed_path, sizeof(published[i].managed_path),
|
|
"%s/%s", state->project_path, published[i].asset.path);
|
|
if (length <= 0 || static_cast<size_t>(length) >= sizeof(published[i].managed_path))
|
|
return LARDON3D_ACQUISITION_INGEST_INVALID_ARGUMENT;
|
|
if (lardon3d_acquisition_extract_metadata(published[i].managed_path,
|
|
&published[i].metadata) != LARDON3D_ACQUISITION_OK)
|
|
return LARDON3D_ACQUISITION_INGEST_METADATA_ERROR;
|
|
#ifdef LARDON3D_ACQUISITION_INGEST_TESTING
|
|
if (testing_metadata != nullptr) {
|
|
if (testing_metadata_count != source_count) return LARDON3D_ACQUISITION_INGEST_INTERNAL_ERROR;
|
|
published[i].metadata = testing_metadata[i];
|
|
}
|
|
#endif
|
|
}
|
|
|
|
bool assigned[LARDON3D_ACQUISITION_INGEST_MAX_SOURCES]{};
|
|
if (options->grouping == LARDON3D_ACQUISITION_GROUP_CALLER_EXPLICIT) {
|
|
for (size_t i = 0; i < source_count; ++i) {
|
|
if (assigned[i]) continue;
|
|
Lardon3DAcquisitionIngestGroup &group = output->groups[output->group_count++];
|
|
group.basis = LARDON3D_ACQUISITION_GROUP_EXPLICIT;
|
|
for (size_t j = i; j < source_count; ++j) {
|
|
if (sources[j].explicit_group == sources[i].explicit_group) {
|
|
(void)add_to_group(group, j);
|
|
assigned[j] = true;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
size_t strong_count[LARDON3D_ACQUISITION_INGEST_MAX_SOURCES]{};
|
|
size_t strong_partner[LARDON3D_ACQUISITION_INGEST_MAX_SOURCES]{};
|
|
for (size_t i = 0; i < source_count; ++i) for (size_t j = i + 1; j < source_count; ++j) {
|
|
Lardon3DAcquisitionPairResult pair{};
|
|
if (lardon3d_acquisition_compare(&published[i].metadata, &published[j].metadata, 0, 0,
|
|
&pair) != LARDON3D_ACQUISITION_OK)
|
|
return LARDON3D_ACQUISITION_INGEST_INTERNAL_ERROR;
|
|
if (pair.decision == LARDON3D_ACQUISITION_SAME_ACQUISITION_STRONG) {
|
|
++strong_count[i]; ++strong_count[j]; strong_partner[i] = j; strong_partner[j] = i;
|
|
}
|
|
}
|
|
for (size_t i = 0; i < source_count; ++i) {
|
|
if (assigned[i]) continue;
|
|
Lardon3DAcquisitionIngestGroup &group = output->groups[output->group_count++];
|
|
(void)add_to_group(group, i); assigned[i] = true;
|
|
size_t partner = strong_partner[i];
|
|
if (strong_count[i] == 1u && strong_count[partner] == 1u && strong_partner[partner] == i) {
|
|
(void)add_to_group(group, partner); assigned[partner] = true;
|
|
group.basis = LARDON3D_ACQUISITION_GROUP_STRONG;
|
|
} else group.basis = LARDON3D_ACQUISITION_GROUP_SINGLETON;
|
|
}
|
|
}
|
|
if (options->resume_capture_id != 0u && output->group_count != 1u)
|
|
return LARDON3D_ACQUISITION_INGEST_INVALID_ARGUMENT;
|
|
|
|
for (size_t group_index = 0; group_index < output->group_count; ++group_index) {
|
|
Lardon3DAcquisitionIngestGroup &group = output->groups[group_index];
|
|
Lardon3DProjectDbCapture capture{};
|
|
if (options->resume_capture_id != 0u) {
|
|
database_result = lardon3d_project_db_load_capture(state->project_db,
|
|
options->resume_capture_id, &capture);
|
|
if (database_result != LARDON3D_PROJECT_DB_OK) return db_result(database_result);
|
|
if (capture.scanset_id != scanset_id) return LARDON3D_ACQUISITION_INGEST_CONSTRAINT;
|
|
} else {
|
|
database_result = lardon3d_project_db_create_capture(state->project_db, scanset_id,
|
|
options->imported_at, &capture);
|
|
if (database_result != LARDON3D_PROJECT_DB_OK) return db_result(database_result);
|
|
}
|
|
group.capture_id = capture.capture_id;
|
|
size_t jpeg = SIZE_MAX, raw = SIZE_MAX;
|
|
for (size_t member = 0; member < group.source_count; ++member) {
|
|
size_t index = group.source_indices[member];
|
|
Lardon3DProjectDbCaptureSourceKind source_kind{};
|
|
if (published[index].metadata.source_kind == LARDON3D_ACQUISITION_SOURCE_JPEG)
|
|
source_kind = LARDON3D_DB_CAPTURE_SOURCE_JPEG;
|
|
else if (published[index].metadata.source_kind == LARDON3D_ACQUISITION_SOURCE_RAW)
|
|
source_kind = LARDON3D_DB_CAPTURE_SOURCE_RAW;
|
|
else
|
|
return LARDON3D_ACQUISITION_INGEST_CONSTRAINT;
|
|
// S3-E commits SOURCE membership with the already validated source kind.
|
|
// Downstream RAW work may use only this explicit relation; path, hash,
|
|
// basename, attachment order, and image identity cannot replace it.
|
|
database_result = lardon3d_project_db_record_capture_source_asset(
|
|
state->project_db, capture.capture_id, published[index].asset.asset_id, source_kind);
|
|
if (database_result != LARDON3D_PROJECT_DB_OK) return db_result(database_result);
|
|
if (published[index].metadata.source_kind == LARDON3D_ACQUISITION_SOURCE_JPEG && jpeg == SIZE_MAX)
|
|
jpeg = index;
|
|
if (published[index].metadata.source_kind == LARDON3D_ACQUISITION_SOURCE_RAW && raw == SIZE_MAX)
|
|
raw = index;
|
|
}
|
|
bool use_jpeg = jpeg != SIZE_MAX &&
|
|
(options->representation == LARDON3D_ACQUISITION_SELECT_JPEG_SOURCE || raw == SIZE_MAX);
|
|
if (use_jpeg) {
|
|
Lardon3DProjectDbImage image{};
|
|
Lardon3DProjectDbImageRegisterStatus status{};
|
|
database_result = lardon3d_project_db_publish_source_capture_image(
|
|
state->project_db, capture.capture_id, published[jpeg].asset.asset_id,
|
|
base_name(sources[jpeg].source_path), sources[jpeg].source_path,
|
|
options->producer_task_id, options->imported_at, options->select_representation != 0u,
|
|
&status, &image);
|
|
if (database_result != LARDON3D_PROJECT_DB_OK) return db_result(database_result);
|
|
} else if (raw != SIZE_MAX) {
|
|
Lardon3DRawDevelopmentOutput developed{};
|
|
Lardon3DRawDevelopmentResult result = lardon3d_raw_develop_to_capture(
|
|
state, capture.capture_id, published[raw].managed_path, options->producer_task_id,
|
|
options->imported_at, &developed);
|
|
if (result != LARDON3D_RAW_DEVELOPMENT_OK)
|
|
return LARDON3D_ACQUISITION_INGEST_DEVELOPMENT_ERROR;
|
|
if (options->select_representation) {
|
|
database_result = lardon3d_project_db_set_selected_capture_image(
|
|
state->project_db, capture.capture_id, developed.image.image_id);
|
|
if (database_result != LARDON3D_PROJECT_DB_OK) return db_result(database_result);
|
|
}
|
|
} else return LARDON3D_ACQUISITION_INGEST_CONSTRAINT;
|
|
}
|
|
return LARDON3D_ACQUISITION_INGEST_OK;
|
|
}
|