33 addUsageLine(
"Find alignment of the experimental images in respect to a set of references");
35 addParamsLine(
" -i <md_file> : Metadata file with the experimental images");
36 addParamsLine(
" -r <md_file> : Metadata file with the reference images");
37 addParamsLine(
" -o <md_file> : Resulting metadata file with the aligned images");
38 addParamsLine(
" [--thr <N=-1>] : Maximal number of the processing CPU threads");
39 addParamsLine(
" [--angDistance <a=10>] : Angular distance");
40 addParamsLine(
" [--odir <outputDir=\".\">] : Output directory");
41 addParamsLine(
" [--keepBestN <N=1>] : For each image, store N best alignments to references. N must be smaller than no. of references");
42 addParamsLine(
" [--allowInputSwap] : Allow swapping reference and experimental images");
43 addParamsLine(
" [--useWeightInsteadOfCC] : Select the best reference using weight, instead of CC");
44 addParamsLine(
" [--oUpdatedRefs <baseName=\"\">]: Update references using assigned experimental images. Store result here");
49 m_imagesToAlign.fn = getParam(
"-i");
50 m_referenceImages.fn = getParam(
"-r");
51 auto outDir =
FileName(getParam(
"--odir"));
52 if ( ! outDir.exists()) {
53 if (outDir.makePath()) {
57 m_fnOut = outDir +
"/" + std::string(getParam(
"-o"));
58 m_angDistance = getDoubleParam(
"--angDistance");
59 m_noOfBestToKeep = getIntParam(
"--keepBestN");
60 m_allowDataSwap = checkParam(
"--allowInputSwap");
61 m_useWeightInsteadOfCC = checkParam(
"--useWeightInsteadOfCC");
62 m_updateHelper.doUpdate = checkParam(
"--oUpdatedRefs");
63 if (m_updateHelper.doUpdate) {
64 FileName base = outDir +
"/" + std::string(getParam(
"--oUpdatedRefs"));
65 m_updateHelper.fnStk = base +
".stk";
66 m_updateHelper.fnXmd = base +
".xmd";
69 int threads = getIntParam(
"--thr");
73 m_settings.cpuThreads = threads;
79 if (verbose < 1)
return;
81 std::cout <<
"Input metadata : " << m_imagesToAlign.fn <<
"\n";
82 std::cout <<
"Reference metadata : " << m_referenceImages.fn <<
"\n";
83 std::cout <<
"Output metadata : " << m_fnOut <<
"\n";
84 std::cout <<
"Angular distance : " << m_angDistance <<
"\n";
85 std::cout <<
"Best references kept : " << m_noOfBestToKeep <<
"\n";
86 if (m_updateHelper.doUpdate) {
87 std::cout <<
"Updated references : " << m_updateHelper.fnXmd <<
"\n";
97 size_t origN = md.
size();
107 if (IS_REF && (origN != Ndim)) {
108 std::cerr << h.fn <<
" contains disabled images. This is not expected and might lead to wrong result\n";
111 auto dims =
Dimensions(Xdim, Ydim, Zdim, Ndim);
112 auto dimsCropped =
Dimensions((Xdim / 2) * 2, (Ydim / 2) * 2, Zdim, Ndim);
113 bool mustCrop = (dims != dimsCropped);
114 h.dims = dimsCropped;
117 h.data = std::unique_ptr<T[]>(
new T[dims.size()]);
118 auto ptr = h.data.get();
120 auto routine = [&dims, ptr]
121 (
int thrId,
const FileName &fn,
size_t storeIndex) {
122 size_t offset = storeIndex * dims.sizeSingle();
128 std::vector<Image<T>> tmpImages;
129 tmpImages.reserve(m_threadPool.size());
131 std::cerr <<
"We need an even input (sizes must be multiple of two). Input will be cropped\n";
132 for (
size_t t = 0; t < m_threadPool.size(); ++t) {
133 tmpImages.emplace_back(Xdim, Ydim);
137 auto routineCrop = [&dims, &dimsCropped, ptr, &tmpImages]
138 (
int thrId,
const FileName &fn,
size_t storeIndex) {
140 tmpImages.at(thrId).read(fn);
142 const size_t destOffsetN = storeIndex * dimsCropped.sizeSingle();
143 for (
size_t y = 0;
y < dimsCropped.y(); ++
y) {
144 size_t srcOffsetY =
y * dims.x();
145 size_t destOffsetY =
y * dimsCropped.x();
146 memcpy(ptr + destOffsetN + destOffsetY,
147 tmpImages.at(thrId).data.data + srcOffsetY,
148 dimsCropped.x() *
sizeof(T));
155 auto futures = std::vector<std::future<void>>();
156 futures.reserve(Ndim);
159 std::cerr <<
"No roration specified for reference images. Using 0 by default\n";
160 h.rots.resize(Ndim, 0);
164 h.tilts.reserve(Ndim);
166 std::cerr <<
"No tilt specified for reference images. Using 0 by default\n";
167 h.tilts.resize(Ndim, 0);
176 for (
size_t objId : md.
ids()) {
179 h.rowIds.emplace_back(objId);
182 futures.emplace_back(m_threadPool.push(routineCrop, fileNames.at(i),
i));
184 futures.emplace_back(m_threadPool.push(routine, fileNames.at(i),
i));
189 for (
auto &
f : futures) {
200 if (isRefData && (! h.md.containsLabel(
MDL_REF))) {
207 if ( ! m_referenceImages.dims.equalExceptNPadded(m_imagesToAlign.dims)) {
210 if (m_noOfBestToKeep > m_referenceImages.dims.n()) {
213 if (m_referenceImages.dims.n() <= 1) {
219 template<
bool IS_ESTIMATION_TRANSPOSED>
221 const std::vector<AlignmentEstimation> &est) {
222 const size_t noOfRefs = m_referenceImages.dims.n();
223 m_weights.resize(noOfRefs);
226 for (
size_t r = 0; r < noOfRefs; ++r) {
227 computeWeightsAndSave<IS_ESTIMATION_TRANSPOSED>(est, r);
232 template<
bool IS_ESTIMATION_TRANSPOSED>
234 const std::vector<AlignmentEstimation> &est,
236 const size_t noOfRefs = m_referenceImages.dims.n();
237 const size_t noOfSignals = m_imagesToAlign.dims.n();
240 auto getAngle = [&](
size_t index) {
242 m_referenceImages.rots.at(refIndex),
243 m_referenceImages.tilts.at(refIndex),
245 m_referenceImages.rots.at(
index),
246 m_referenceImages.tilts.at(
index),
253 auto mask = std::vector<bool>(noOfRefs,
false);
254 for (
size_t r = 0; r < noOfRefs; ++r) {
256 || (getAngle(r) <= m_angDistance)) {
263 auto figsOfMerit = std::vector<WeightCompHelper>();
264 figsOfMerit.reserve(count * noOfSignals);
267 for (
size_t r = 0; r < noOfRefs; ++r) {
270 for (
size_t s = 0; s < noOfSignals; ++s) {
271 if (IS_ESTIMATION_TRANSPOSED) {
272 figsOfMerit.emplace_back(est.at(s).figuresOfMerit.at(r), r, s);
274 figsOfMerit.emplace_back(est.at(r).figuresOfMerit.at(s), r, s);
279 computeWeightsAndSave(figsOfMerit, refIndex);
284 std::vector<WeightCompHelper> &figsOfMerit,
286 const size_t noOfSignals = m_imagesToAlign.dims.n();
287 auto &weights = m_weights.at(refIndex);
288 weights = std::vector<float>(noOfSignals, 0);
289 const size_t noOfNumbers = figsOfMerit.size();
292 std::sort(figsOfMerit.begin(), figsOfMerit.end(),
293 [](
const WeightCompHelper &l,
const WeightCompHelper &r) {
294 return l.merit < r.merit;
296 auto invMaxMerit = 1.f / figsOfMerit.back().merit;
299 for (
size_t c = 0;
c < noOfNumbers; ++
c) {
300 const auto &tmp = figsOfMerit.at(
c);
301 if (tmp.refIndex != refIndex) {
305 float cdf =
c / (float)(noOfNumbers - 1);
306 float merit = tmp.merit;
308 weights.at(tmp.imgIndex) = merit * invMaxMerit * cdf;
353 fillRow(row, pose, refIndex, weight, imgIndex);
359 std::vector<float> &data,
360 size_t &pos,
float &val) {
367 data.at(pos) = std::numeric_limits<float>::lowest();
371 template<
bool IS_ESTIMATION_TRANSPOSED,
bool USE_WEIGHT>
373 const std::vector<AlignmentEstimation> &est) {
374 const size_t noOfRefs = m_referenceImages.dims.n();
375 auto accessor = [&](
size_t image,
size_t reference) {
377 return m_weights.at(reference).at(image);
378 }
else if (IS_ESTIMATION_TRANSPOSED) {
379 return est.at(image).figuresOfMerit.at(reference);
381 return est.at(reference).figuresOfMerit.at(image);
385 const size_t noOfImages = m_imagesToAlign.dims.n();
386 m_assignments.reserve(noOfImages * m_noOfBestToKeep);
387 for (
size_t i = 0;
i < noOfImages; ++
i) {
389 auto votes = std::vector<float>();
390 votes.reserve(noOfRefs);
391 for (
size_t r = 0; r < noOfRefs; ++r) {
392 votes.emplace_back(accessor(
i, r));
395 for (
size_t nthBest = 0; nthBest < m_noOfBestToKeep; ++nthBest) {
399 extractMax(votes, refIndex, val);
403 const auto &p = IS_ESTIMATION_TRANSPOSED
404 ? est.at(
i).poses.at(refIndex).inv()
405 : est.at(refIndex).poses.at(
i);
406 m_assignments.emplace_back(refIndex,
i,
407 m_weights.at(refIndex).at(
i), val,
414 template<
bool USE_WEIGHT>
416 if (0 == m_assignments.size()) {
421 auto &md = m_imagesToAlign.md;
423 std::sort(m_assignments.begin(), m_assignments.end(),
429 :(l.
merit > r.merit));
432 std::vector<MDRowVec> rows;
433 size_t indexMeta = 0;
434 size_t indexAssign = 0;
435 rows.reserve(m_assignments.size());
436 for (
const auto& _row : md) {
437 const auto& row =
dynamic_cast<const MDRowVec&
>(_row);
439 if (indexMeta != m_assignments.at(indexAssign).imgIndex) {
445 auto maxVote = m_assignments.at(indexAssign).merit;
446 while (m_assignments.at(indexAssign).imgIndex == indexMeta) {
448 rows.emplace_back(row);
449 auto &r = rows.back();
450 const auto &
a = m_assignments.at(indexAssign);
451 fillRow(r,
a.pose,
a.refIndex,
a.weight,
a.imgIndex, maxVote);
454 if (indexAssign == m_assignments.size()) {
462 const auto labels = rows.at(0).labels();
465 mdOut.
write(m_fnOut);
470 m_settings.refDims = m_referenceImages.dims;
471 m_settings.otherDims = m_imagesToAlign.dims;
476 const Dimensions &dims = m_referenceImages.dims;
477 const auto &fn = m_updateHelper.fnStk;
479 for (
size_t n = 0;
n < dims.
n(); ++
n ) {
484 MultidimArray<T> wrapper(1, 1, dims.
y(), dims.
x(), m_referenceImages.data.get() + offset);
492 const auto &fn = m_updateHelper.fnXmd;
495 m_updateHelper.refBlock.write(
"classes@" + fn,
MD_APPEND);
497 const size_t noOfBlocks = m_updateHelper.imgBlocks.size();
498 const auto pattern =
"class%06d_images@";
499 for (
size_t n = 0;
n < noOfBlocks; ++
n) {
500 const auto &md = m_updateHelper.imgBlocks.at(
n);
501 if (0 == md.size()) {
513 std::lock_guard<std::mutex> lk(mutex);
515 const size_t indexInStk = refIndex + 1;
517 auto &refMeta = m_updateHelper.refBlock;
519 refName.
compose(indexInStk, m_updateHelper.fnStk);
523 refRow.setValue(
MDL_REF, getRefMetaIndex(refIndex),
true);
524 refRow.setValue(
MDL_IMAGE, refName,
true);
526 refMeta.addRows({refRow});
533 const size_t noOfImages = images.size();
534 if (0 != noOfImages) {
535 std::vector<MDRowVec> rows(noOfImages);
536 for (
size_t i = 0;
i < noOfImages; ++
i) {
537 auto &row = rows.at(
i);
538 const auto &
a = images.at(
i);
539 getImgRow(row,
a.imgIndex);
540 fillRow(row,
a.pose, refIndex,
a.weight,
a.imgIndex);
542 const auto& labels = rows.at(0).labels();
543 if (0 == m_updateHelper.imgBlocks.size()) {
544 m_updateHelper.imgBlocks.resize(m_referenceImages.dims.n(), labels);
546 auto &md = m_updateHelper.imgBlocks.at(refIndex);
554 std::cerr << fn <<
" exists. It will be overwritten.\n";
561 if (1 < m_noOfBestToKeep) {
562 std::cout <<
"Each experimental image will contribute to more than one reference image.\n";
565 m_updateHelper.imgBlocks.clear();
568 updateRefs(m_referenceImages.data.get(), m_imagesToAlign.data.get(), m_assignments);
577 m_threadPool.resize(getSettings().cpuThreads);
579 load<false>(m_imagesToAlign);
580 load<true>(m_referenceImages);
582 bool hasMoreReferences = m_allowDataSwap
583 && (m_referenceImages.dims.n() > m_imagesToAlign.dims.n());
584 if (hasMoreReferences) {
585 std::cerr <<
"We are swapping reference images and experimental images. " 586 "This will enhance the performance. This might lead to worse results if the experimental " 587 "images are not well centered. Use it with care!\n";
588 std::swap(m_referenceImages, m_imagesToAlign);
594 auto alignment =
align(m_referenceImages.data.get(), m_imagesToAlign.data.get());
597 if (hasMoreReferences) {
598 std::swap(m_referenceImages, m_imagesToAlign);
599 computeWeights<true>(alignment);
600 if (m_useWeightInsteadOfCC) {
601 computeAssignment<true, true>(alignment);
603 computeAssignment<true, false>(alignment);
606 computeWeights<false>(alignment);
607 if (m_useWeightInsteadOfCC) {
608 computeAssignment<false, true>(alignment);
610 computeAssignment<false, false>(alignment);
617 if (m_useWeightInsteadOfCC) {
618 storeAlignedImages<true>();
620 storeAlignedImages<false>();
623 if (m_updateHelper.doUpdate) {
#define REPORT_ERROR(nerr, ErrormMsg)
Couldn't write to file.
void compose(const String &str, const size_t no, const String &ext="")
void inv(Matrix2D< T > &result) const
static unsigned findCores()
virtual void check() const
virtual void readParams() override
CUDA_HD constexpr size_t x() const
CUDA_HD constexpr size_t sizeSingle() const
virtual void defineParams() override
void write(const FileName &fn) const
T Euler_distanceBetweenAngleSets(T rot1, T tilt1, T psi1, T rot2, T tilt2, T psi2, bool only_projdir)
void max(Image< double > &op1, const Image< double > &op2)
virtual void show() const override
CUDA_HD constexpr size_t y() const
void sort(struct DCEL_T *dcel)
CUDA_HD constexpr size_t n() const
void setValue(MDLabel label, const T &d, bool addLabel=true)
T align(T number, uint32_t alignment)
void updateRefXmd(size_t refIndex, std::vector< Assignment > &images)
String formatString(const char *format,...)
virtual void run() override
check(nparam, nf, nfsr, &Linfty, nineq, nineqn, neq, neqn, ncsrl, ncsrn, mode, &modem, eps, bgbnd, param)
Some logical error in the pipeline.