diff options
| -rw-r--r-- | src/calibration.cpp | 189 | ||||
| -rw-r--r-- | src/calibration.h | 6 | ||||
| -rw-r--r-- | src/camera/icamera.h | 6 | ||||
| -rw-r--r-- | src/camera/veyeimx287m.cpp | 48 | ||||
| -rw-r--r-- | src/camera/veyeimx287m.h | 8 | ||||
| -rw-r--r-- | src/camera/veyeimx287m_types.h | 2 | ||||
| -rw-r--r-- | src/constants.h | 4 | ||||
| -rw-r--r-- | src/dumps.cpp | 131 | ||||
| -rw-r--r-- | src/image.cpp | 136 | ||||
| -rw-r--r-- | src/iscanner.h | 4 | ||||
| -rw-r--r-- | src/main.cpp | 92 | ||||
| -rw-r--r-- | src/pixels.cpp | 19 | ||||
| -rw-r--r-- | src/profile.cpp | 15 | ||||
| -rw-r--r-- | src/profile.h | 6 | ||||
| -rw-r--r-- | src/protocols/httpserver.cpp | 34 | ||||
| -rw-r--r-- | src/scanner.cpp | 12 | ||||
| -rw-r--r-- | src/scanner.h | 8 |
17 files changed, 494 insertions, 226 deletions
diff --git a/src/calibration.cpp b/src/calibration.cpp index 3462565..6e8f695 100644 --- a/src/calibration.cpp +++ b/src/calibration.cpp @@ -4,6 +4,7 @@ #include <execution> #include <iostream> +#include <numeric> #include <QDebug> #include <QDir> @@ -15,7 +16,7 @@ #include "imagealgos.h" -bool openCalibrationTable(const QString &filename, const CalibrationTablePtr table) +bool op::openCalibrationTable(const QString &filename, const op::CalibrationTablePtr table) { QFile f(filename); @@ -26,9 +27,9 @@ bool openCalibrationTable(const QString &filename, const CalibrationTablePtr tab return false; } - auto bytes = f.read((char*) table.data(), sizeof(CalibrationTable)); + auto bytes = f.read((char *) table.data(), sizeof(op::CalibrationTable)); - if (bytes != sizeof(CalibrationTable)) { + if (bytes != sizeof(op::CalibrationTable)) { qWarning() << "cannot read calibration table from" << filename << bytes; if (f.error()) { @@ -44,13 +45,14 @@ bool openCalibrationTable(const QString &filename, const CalibrationTablePtr tab // qDebug() << "calibration column mid:" << col[640]; // } + qDebug() << "calibration table opened:" << filename; + return true; } -bool dump( - const CalibrationTablePtr& table, const QString& filename) +bool op::dump(const op::CalibrationTablePtr &table, const QString &filename) { - qDebug() << Q_FUNC_INFO << "size is" << sizeof(CalibrationTable); + qDebug() << Q_FUNC_INFO << "size is" << sizeof(op::CalibrationTable); QFile f(filename); @@ -61,10 +63,9 @@ bool dump( return false; } - const auto written = f.write((const char*) table.data(), - sizeof(CalibrationTable)); + const auto written = f.write((const char *) table.data(), sizeof(op::CalibrationTable)); - if (written != sizeof(CalibrationTable) || !f.flush()) { + if (written != sizeof(op::CalibrationTable) || !f.flush()) { qWarning() << Q_FUNC_INFO << "cannot write" << filename << ":" << f.errorString(); return false; @@ -73,12 +74,11 @@ bool dump( return true; } -void interpolate(const CalibrationTablePtr table) +void op::interpolate(const op::CalibrationTablePtr table) { - std::for_each(std::execution::par, - table->begin(), - table->end(), - [](auto& column) { interpolate(column); }); + std::for_each(std::execution::par, table->begin(), table->end(), [](auto &column) { + op::interpolate(column); + }); // for (size_t i = 9471; i < 9472; i++) { // std::cout << __func__ << ": row #" << i << ": "; @@ -107,8 +107,7 @@ void interpolate(const CalibrationTablePtr table) // } } -void interpolate( - CalibrationColumn& column) +void op::interpolate(op::CalibrationColumn &column) { size_t start{0}; auto& c = column; @@ -150,8 +149,7 @@ void interpolate( } } -QImage calibrationTableToImage( - const CalibrationTablePtr& calibrationTable) +QImage op::calibrationTableToImage(const op::CalibrationTablePtr &calibrationTable) { QImage result(QSize(calibrationTable->size(), calibrationTable->at(0).size()), @@ -190,14 +188,12 @@ QImage calibrationTableToImage( } qDebug() << "not null count" << notNull << "of" - << sizeof(CalibrationTable) / - sizeof(calibrationTable->at(0).at(0)); + << sizeof(op::CalibrationTable) / sizeof(calibrationTable->at(0).at(0)); return result; } -QList<Pixels> filter( - const QList<Pixels>& rawProfiles) +QList<Pixels> op::filter(const QList<Pixels> &rawProfiles) { QList<Pixels> result; @@ -225,9 +221,9 @@ QList<Pixels> filter( return result; } -bool calibrateZ(const QList<Pixels> &rawProfiles, - const uint32_t &stepsPerMm, - CalibrationTablePtr table) +bool op::calibrateZ(const QList<Pixels> &rawProfiles, + const uint32_t &stepsPerMm, + op::CalibrationTablePtr table) { for (const auto &rawProfile : rawProfiles) { const float positionMm{float(rawProfile.counters.encoderPosition) / @@ -241,7 +237,7 @@ bool calibrateZ(const QList<Pixels> &rawProfiles, uint16_t(pixelValue * discretesInRage / img_height)}; // TODO: move this validation to some better place - if (Q_UNLIKELY(discretePixelValue >= calibrationColumnHeight)) { + if (Q_UNLIKELY(discretePixelValue >= op::calibrationColumnHeight)) { std::cerr << __func__ << ":/tinvalid discrete value. col: " << columnIdx << ", val: " << pixelValue << std::endl; @@ -257,12 +253,15 @@ bool calibrateZ(const QList<Pixels> &rawProfiles, return true; } -bool calibrateX(const QList<Pixels> &rawProfiles, CalibrationTablePtr table) +bool op::calibrateX(const QList<Pixels> &rawProfiles, op::CalibrationTablePtr table) { // TODO: move to settings constexpr double triangleBaseMm{8.}; + int i{-1}; + for (const auto &rawProfile : rawProfiles) { + ++i; const auto& pixels = rawProfile.pixels; auto lines = pixelsToLines(rawProfile); @@ -278,6 +277,7 @@ bool calibrateX(const QList<Pixels> &rawProfiles, CalibrationTablePtr table) xAnchors.begin(), [](const auto& l) { return l.x1(); }); xAnchors.last() = lines.last().x2(); + // qDebug() << "anchors (discretes):" << xAnchors; auto centralAnchorIt = std::min_element(std::execution::par_unseq, xAnchors.constBegin(), @@ -299,6 +299,13 @@ bool calibrateX(const QList<Pixels> &rawProfiles, CalibrationTablePtr table) auto xAnchorIt = xAnchors.constBegin() + 1; auto xAnchorMmIt = xAnchorsMm.constBegin() + 1; + // qDebug() << "anchors (mm):" << xAnchors; + + if (rawProfile.counters.encoderPosition > 6000) { + qt_noop(); + } + + std::array<uint16_t, img_width> dl = {uint16_t{0}}; for (size_t columnIdx = 0; columnIdx < pixels.size(); ++columnIdx) { // skip points with to the left from left line and to the right from @@ -335,7 +342,7 @@ bool calibrateX(const QList<Pixels> &rawProfiles, CalibrationTablePtr table) uint16_t(pixelValue * discretesInRage / img_height)}; // TODO: move this validation to some better place - if (Q_UNLIKELY(discretePixelValue >= calibrationColumnHeight)) { + if (Q_UNLIKELY(discretePixelValue >= op::calibrationColumnHeight)) { std::cerr << __func__ << ":/tinvalid discrete value. col: " << columnIdx << ", val: " << pixelValue << std::endl; @@ -348,15 +355,24 @@ bool calibrateX(const QList<Pixels> &rawProfiles, CalibrationTablePtr table) const auto xRelative = float(columnX - xLeft) / xLineLen; const auto xMmValue = xLeftMm + xRelative * (triangleBaseMm / 2.); + // TODO: check if value exists (*table)[columnIdx][discretePixelValue] = xMmValue; + dl[columnIdx] = discretePixelValue; } + + // const auto avg_dpv = std::accumulate(dl.cbegin(), dl.cend(), uint64_t{0}) / img_width; + // const auto avg_p = std::accumulate(pixels.cbegin(), pixels.cend(), double{0.}) / img_width; + // std::cout << avg_dpv << '/' << avg_p << std::endl; + // qDebug() << rawProfile; } + std::cout << std::endl; + return true; } -void dumpCalibrationPixels(const std::vector<std::shared_ptr<Pixels> > &calibrationPixels, - const DumpFormat format) +void op::dumpCalibrationPixels(const std::vector<std::shared_ptr<Pixels> > &calibrationPixels, + const op::DumpFormat format) { const auto &rawProfiles = calibrationPixels; // std::vector<Pixels> rawProfiles; @@ -381,7 +397,7 @@ void dumpCalibrationPixels(const std::vector<std::shared_ptr<Pixels> > &calibrat // TODO: move out of `for` loop // TODO: use switch, or remove enum and create a separate function - if (format == DumpFormat::Json) { + if (format == op::DumpFormat::Json) { QFile f{filepath}; if (!f.open(QFile::WriteOnly)) { @@ -417,7 +433,7 @@ void dumpCalibrationPixels(const std::vector<std::shared_ptr<Pixels> > &calibrat } qDebug() << "file written: " << f.fileName(); - } else if (format == DumpFormat::Binary) { + } else if (format == op::DumpFormat::Binary) { if (!rawProfile->save(filepath)) { return; } @@ -426,3 +442,112 @@ void dumpCalibrationPixels(const std::vector<std::shared_ptr<Pixels> > &calibrat qDebug() << "dump finished"; } + +bool areAllEncPosIdentical(const std::vector<Pixels> &rawProfiles) +{ + if (rawProfiles.empty()) { + return false; + } + + if (rawProfiles.size() == 1) { + return true; + } + + // const auto equalEncPos = [](const Pixels &a, const Pixels &b) { + // return a.counters.encoderPosition == b.counters.encoderPosition; + // }; + const auto nonEqualEncPos = [](const Pixels &a, const Pixels &b) { + return a.counters.encoderPosition != b.counters.encoderPosition; + }; + + return std::ranges::adjacent_find(rawProfiles, nonEqualEncPos) == rawProfiles.end(); +} + +std::optional<Pixels> op::avgFilterAtPos(const std::vector<Pixels> &rawProfiles) +{ + if (rawProfiles.empty()) { + return std::nullopt; + } + + if (rawProfiles.size() == 1) { + return rawProfiles.front(); + } + + if (!areAllEncPosIdentical(rawProfiles)) { + return std::nullopt; + } + + Pixels sum; + sum.counters = rawProfiles.front().counters; + + // TODO: think about std::accumulate + // const auto tmp = std::accumulate(rawProfiles.begin(), rawProfiles.end(), Pixels{}); + + for (const auto &rp : rawProfiles) { + sum += rp; + } + + sum /= rawProfiles.size(); + + return sum; +} + +std::optional<Pixels> op::medianFilterAtPos(const std::vector<Pixels> &rawProfiles) +{ + const auto &rps = rawProfiles; + + if (rps.empty()) { + return std::nullopt; + } + + if (rps.size() == 1) { + return rps.front(); + } + + if (!areAllEncPosIdentical(rps)) { + return std::nullopt; + } + + using pixel_t = decltype(Pixels::pixels)::value_type; + // std::array<std::vector<pixel_t>, img_width> forMedian; + + Pixels result; + + for (size_t i{0}; i < img_width; ++i) { + std::vector<pixel_t> pixelsAtI; + pixelsAtI.reserve(rps.size()); + + for (const auto &rp : rps) { + const auto &p = rp.pixels.at(i); + + if (qFuzzyIsNull(p) || qIsNaN(p)) { + continue; + } + pixelsAtI.push_back(p); + } + + // result.pixels.at(i) = pixelsAtI.empty() ? 0 : + if (pixelsAtI.empty()) { + continue; + } + + if (pixelsAtI.size() == 1) { + result.pixels.at(i) = pixelsAtI.front(); + continue; + } + + std::sort(std::execution::par_unseq, pixelsAtI.begin(), pixelsAtI.end()); + + if (pixelsAtI.size() % 2 == 1) { + result.pixels.at(i) = pixelsAtI.at(pixelsAtI.size() / 2); + } else { + result.pixels.at(i) = (pixelsAtI.at(pixelsAtI.size() / 2 - 1) + + pixelsAtI.at(pixelsAtI.size() / 2)) + / 2; + } + } + + result.counters = rps.front().counters; + + return result; +} diff --git a/src/calibration.h b/src/calibration.h index 113cad9..fd16404 100644 --- a/src/calibration.h +++ b/src/calibration.h @@ -7,6 +7,7 @@ #include "constants.h" #include "pixels.h" +namespace op { using CalibrationColumn = std::array<float, calibrationTableHeight>; // map [0; discretesInRage] (discretes) to [0; zRangeMm] (mm) using CalibrationTable = std::array<CalibrationColumn, img_width>; @@ -28,6 +29,10 @@ bool dump(const CalibrationTablePtr &table, const QString &filename); */ QList<Pixels> filter(const QList<Pixels> &rawProfiles); +// TODO: move somewhere or combine with dumps.{h,cpp} +std::optional<Pixels> avgFilterAtPos(const std::vector<Pixels> &rawProfiles); +std::optional<Pixels> medianFilterAtPos(const std::vector<Pixels> &rawProfiles); + bool calibrateX(const QList<Pixels> &rawProfiles, CalibrationTablePtr table); bool calibrateZ(const QList<Pixels> &rawProfiles, const uint32_t &stepsPerMm, @@ -43,3 +48,4 @@ namespace { // static CalibrationTablePtr calibrationTableZ; // static CalibrationTablePtr calibrationTableX; } // namespace +} // namespace op
\ No newline at end of file diff --git a/src/camera/icamera.h b/src/camera/icamera.h index 94408b6..daee920 100644 --- a/src/camera/icamera.h +++ b/src/camera/icamera.h @@ -67,6 +67,12 @@ public: [[nodiscard]] virtual std::optional<const std::chrono::microseconds> get_triggerExposureDelay() = 0; + [[nodiscard]] virtual bool set_isBlackLevelManual(const bool manual) = 0; + [[nodiscard]] virtual std::optional<bool> get_isBlackLevelManual() = 0; + + [[nodiscard]] virtual bool set_blackLevel(const uint32_t level) = 0; + [[nodiscard]] virtual std::optional<uint32_t> get_blackLevel() = 0; + [[nodiscard]] virtual std::shared_ptr<Image> getImage() = 0; public: diff --git a/src/camera/veyeimx287m.cpp b/src/camera/veyeimx287m.cpp index 4556112..83ad32f 100644 --- a/src/camera/veyeimx287m.cpp +++ b/src/camera/veyeimx287m.cpp @@ -426,6 +426,7 @@ std::optional<float> VeyeIMX287m::get_gain() bool VeyeIMX287m::set_triggerExposureDelay(const std::chrono::microseconds us) { using namespace veye::imx287m; + std::cout << __func__ << ": " << us << std::endl; return m_i2c->write(static_cast<uint16_t>(Register::Trigger_Exp_Delay), us.count()); } @@ -442,6 +443,49 @@ std::optional<const std::chrono::microseconds> VeyeIMX287m::get_triggerExposureD return std::chrono::microseconds{*value}; } +bool VeyeIMX287m::set_isBlackLevelManual(const bool manual) +{ + using namespace veye::imx287m; + std::cout << __func__ << ": " << manual << std::endl; + return m_i2c->write(static_cast<uint16_t>(Register::BLC_Mode), + static_cast<uint32_t>(manual + ? BlackLevelCalibrationMode::ManuallySpecified + : BlackLevelCalibrationMode::AutomaticOrDefault)); +} + +std::optional<bool> VeyeIMX287m::get_isBlackLevelManual() +{ + using namespace veye::imx287m; + + const auto value = m_i2c->read(static_cast<uint32_t>(Register::BLC_Mode)); + + if (!value) { + return {}; + } + + return *value == static_cast<uint32_t>(BlackLevelCalibrationMode::ManuallySpecified); +} + +bool VeyeIMX287m::set_blackLevel(const uint32_t level) +{ + using namespace veye::imx287m; + std::cout << __func__ << ": " << level << std::endl; + return m_i2c->write(static_cast<uint16_t>(Register::Black_Level), level); +} + +std::optional<uint32_t> VeyeIMX287m::get_blackLevel() +{ + using namespace veye::imx287m; + + const auto value = m_i2c->read(static_cast<uint32_t>(Register::Black_Level)); + + if (!value) { + return {}; + } + + return value; +} + bool VeyeIMX287m::openCam() { m_cam_fd = open(videoDevice, O_RDWR); @@ -739,7 +783,7 @@ void VeyeIMX287m::calcPixelsLoop(std::stop_token stopToken) tmp.insert(tmp.begin(), p.cbegin(), p.cend()); } // dumpCalibrationPixels(m_calibrationPixels); - dumpCalibrationPixels(tmp, DumpFormat::Binary); + op::dumpCalibrationPixels(tmp, op::DumpFormat::Binary); m_calibrationPixels.clear(); } } @@ -760,7 +804,7 @@ bool VeyeIMX287m::dequeueImageBuffer(size_t &imageIndex) // TODO: move this shit to some beautiful place if (elapsedTime > 1000. && processedCounter != 0) { - if (false) { + if (true) { fprintf(stderr, "fps: %d\tdropped: %lu sec: %ld " "dq: %lu get: %lu rot: %lu pix: %lu sum: %lu corr: " diff --git a/src/camera/veyeimx287m.h b/src/camera/veyeimx287m.h index 326a43a..764ede8 100644 --- a/src/camera/veyeimx287m.h +++ b/src/camera/veyeimx287m.h @@ -94,6 +94,12 @@ public: [[nodiscard]] bool set_triggerExposureDelay(const std::chrono::microseconds us) override; [[nodiscard]] std::optional<const std::chrono::microseconds> get_triggerExposureDelay() override; + [[nodiscard]] bool set_isBlackLevelManual(const bool manual) override; + [[nodiscard]] std::optional<bool> get_isBlackLevelManual() override; + + [[nodiscard]] bool set_blackLevel(const uint32_t level) override; + [[nodiscard]] std::optional<uint32_t> get_blackLevel() override; + public: /*! * \brief processedCounter - count of images processed in current second. @@ -161,7 +167,7 @@ private: std::jthread m_getThreads[1]; // std::jthread m_getThreads[4]; // TODO: sync all loops somehow to guarantee frames order - std::jthread m_rotateThreads[2]; + std::jthread m_rotateThreads[1]; std::jthread m_calcPixelsThreads[1]; std::mutex m_lastImageMtx; std::shared_ptr<Image> m_lastProcessedImage{}; diff --git a/src/camera/veyeimx287m_types.h b/src/camera/veyeimx287m_types.h index 8cb74ba..a19a8ea 100644 --- a/src/camera/veyeimx287m_types.h +++ b/src/camera/veyeimx287m_types.h @@ -138,6 +138,8 @@ enum class GainMode : uint32_t { AutoGainContinious = 2 }; +enum class BlackLevelCalibrationMode : uint32_t { AutomaticOrDefault = 0, ManuallySpecified = 1 }; + class Params : public QObject { // Q_OBJECT diff --git a/src/constants.h b/src/constants.h index 635243e..d59d189 100644 --- a/src/constants.h +++ b/src/constants.h @@ -23,8 +23,8 @@ constexpr size_t radxa_raw_img_size = radxa_raw_img_stride * img_height; constexpr uint32_t patternSize = 16; // constexpr float hardcodedZRangeMm{175.f}; -// constexpr float hardcodedZRangeMm{200}; -constexpr float hardcodedZRangeMm{50.f}; +constexpr float hardcodedZRangeMm{200}; +// constexpr float hardcodedZRangeMm{50.f}; constexpr size_t calibrationTableHeight{0x4000}; // 16384 namespace { diff --git a/src/dumps.cpp b/src/dumps.cpp index 3e138b3..90716b9 100644 --- a/src/dumps.cpp +++ b/src/dumps.cpp @@ -10,6 +10,9 @@ #include "fuck_intel.h" +#include "calibration.h" + +#include <chrono> #include <execution> #include <iostream> @@ -55,56 +58,116 @@ QList<Pixels> openDump( } if (false) { - filenames = filenames.first(10000); + filenames = filenames.first(1000); } - std::sort(filenames.begin(), filenames.end(), [](const auto &a, const auto &b) { - const auto mca = a.split('_').at(3).toInt(); - const auto mcb = b.split('_').at(3).toInt(); + using mc_t = decltype(Counters::measurementCounter); + using ep_t = decltype(Counters::encoderPosition); + using counters_tuple_t = std::tuple<mc_t, ep_t>; + std::vector<counters_tuple_t> metaFilenames{static_cast<size_t>(filenames.size())}; + + const auto fn2meta = [](const QString &filename) -> counters_tuple_t { + const auto splitted = filename.split('_'); + const auto mc = splitted.at(3).toUInt(); + const auto ep = splitted.at(5).toUInt(); + return std::make_tuple(mc, ep); + }; + + const auto meta2fn = [](const counters_tuple_t &meta) { + const auto &[mc, ep] = meta; + const auto tmp = QStringLiteral("raw_profile_meas_%1_enc_%2").arg(mc).arg(ep); + return tmp; + }; + + std::transform(filenames.cbegin(), filenames.cend(), metaFilenames.begin(), fn2meta); + + filenames.clear(); + filenames.squeeze(); + + std::sort(metaFilenames.begin(), metaFilenames.end(), [](const auto &a, const auto &b) { + const auto &[mca, epa] = a; + const auto &[mcb, epb] = b; return mca < mcb; }); - qDebug() << __func__ << "create results array" << filenames.size(); - auto resultOptionals = QScopedPointer( - new QList<std::optional<Pixels>>(filenames.size())); - qDebug() << __func__ << "created results array"; + // std::multimap<ep_t, mc_t> enc_meas; + std::map<ep_t, std::vector<mc_t>> enc_meas; + + for (const auto &mf : metaFilenames) { + const auto &[mc, ep] = mf; + enc_meas[ep].push_back(mc); + } + + metaFilenames.clear(); + metaFilenames.shrink_to_fit(); + + QList<Pixels> result; + result.reserve(static_cast<qsizetype>(enc_meas.size())); QElapsedTimer t; t.start(); qDebug() << __func__ << "open real files"; - // std::cout << "here" << std::endl; + uint8_t lastPercent{std::numeric_limits<decltype(lastPercent)>::max()}; + auto lastPercentTime = std::chrono::system_clock::now(); - std::transform(/*std::execution::par_unseq,*/ - filenames.begin(), - filenames.end(), - resultOptionals->begin(), - [dirToRead](const auto &filename) { - // std::cout << '.'; - // auto rawProfile = openRawProfile(dirToRead + "/" + filename); - auto rawProfile = Pixels::load(dirToRead + '/' + filename); + // for (const auto &[ep, mcl] : enc_meas) { + for (auto em = enc_meas.cbegin(); em != enc_meas.cend(); ++em) { + const auto &[ep, mcl] = *em; + std::vector<Pixels> toFilter; + toFilter.reserve(mcl.size()); - return rawProfile; - }); + for (const auto &mc : mcl) { + const auto &filename = meta2fn(std::make_tuple(mc, ep)); + const auto pixelsOpt = Pixels::load(dirToRead + '/' + filename); - filenames.clear(); - filenames.squeeze(); + if (pixelsOpt.has_value()) { + toFilter.push_back(*pixelsOpt); + } + }; + + // const auto filtered = op::avgFilterAtPos(std::move(toFilter)); + const auto filtered = op::medianFilterAtPos(std::move(toFilter)); + + if (filtered.has_value()) { + result.push_back(*filtered); + } + + const uint8_t percent{ + static_cast<uint8_t>(std::distance(enc_meas.cbegin(), em) * 100 / enc_meas.size())}; + + if (percent != lastPercent) { + lastPercent = percent; + const auto now = std::chrono::system_clock::now(); + const auto diffMs = std::chrono::duration_cast<std::chrono::milliseconds>( + now - lastPercentTime) + .count(); + lastPercentTime = std::chrono::system_clock::now(); + qDebug().nospace() << Q_FUNC_INFO << ": " << lastPercent + << "%; count: " << result.size() << "; ms (1%): " << diffMs; + } + } - qDebug() << Q_FUNC_INFO << "open raw profiles: elapsed (ms)" << t.elapsed(); - // std::cout << std::endl; + // std::sort(filenames.begin(), filenames.end(), [](const auto &a, const auto &b) { + // const auto mca = a.split('_').at(3).toInt(); + // const auto mcb = b.split('_').at(3).toInt(); + // return mca < mcb; + // }); - // FIXME: no effect - std::remove_if(std::execution::par, - resultOptionals->begin(), - resultOptionals->end(), - [](auto &a) { return !a.has_value(); }); + // for (size_t i{0}; i < metaFilenames.size(); ++i) { + // const auto &meta = metaFilenames.at(i); + // const auto filename = meta2fn(meta); + // auto rawProfile = Pixels::load(dirToRead + '/' + filename); - QList<Pixels> result(resultOptionals->size()); + // if (rawProfile.has_value()) { + // result << *rawProfile; + // } + // const uint8_t percent{static_cast<uint8_t>(i * 100 / metaFilenames.size())}; - std::transform(std::execution::par, - std::make_move_iterator(resultOptionals->begin()), - std::make_move_iterator(resultOptionals->end()), - result.begin(), - [](auto &p) { return p.value(); }); + // if (percent != lastPercent) { + // lastPercent = percent; + // qDebug().nospace() << Q_FUNC_INFO << ": " << lastPercent << '%'; + // } + // } qDebug() << Q_FUNC_INFO << "elapsed (ms)" << t.elapsed(); diff --git a/src/image.cpp b/src/image.cpp index 636ec78..a111d40 100644 --- a/src/image.cpp +++ b/src/image.cpp @@ -6,6 +6,7 @@ #include <libcamera/formats.h> +#include "image.h" #include "macro.h" #include "pixels.h" @@ -20,15 +21,13 @@ uint64_t rot_elapsed_ns = 0; uint64_t pix_elapsed_ns = 0; uint64_t dropped_count = 0; +float process_column(const Image::column_t &column); +float process_column_center_of_mass(const Image::column_t &column); + // float process_column(const uint8_t (&column)[]) // float process_column(const Image::row_t &column) float process_column(const Image::column_t &column) { - // Image::column_t c = column; - // start_timer(process_column); - // QElapsedTimer t; - // t.start(); - float result = std::numeric_limits<float>::quiet_NaN(); // constexpr uint32_t signalThreshold = 900; // = SKO * sqrt(patternSize) @@ -47,40 +46,6 @@ float process_column(const Image::column_t &column) static_assert((img_height % patternSize) == 0, "img_height % patternSize should be 0"); // std::array<uint16_t, img_height / patternSize> sums; - // uint16_t maxLALASum{0}; - // size_t maxLALAIdx{0}; - // uint16_t sum{0}; - - // for (size_t i = 0; i < img_height; i += patternSize) { - // // const auto sum = std::accumulate(column.cbegin() + i, - // // column.cbegin() + i + patternSize, - // // 0, - // // std::plus<uint16_t>()); - // uint16_t sum{0}; - - // for (size_t j = i; j < i + patternSize; ++j) - // sum += column[j]; - // // if ((i % patternSize) == 0) - // // sum = 0; - // // sum += column[i]; - - // // if (sum > 0xff) - // // std::cout << sum << ' '; - - // if (sum > maxLALASum) { - // maxLALASum = sum; - // maxLALAIdx = i; - // } - // } - - // maxLALAIdx = patternSize * 32 - 14; // crash - // maxLALAIdx = patternSize * 32 - 14; // no crash - - // maxLALAIdx = std::clamp(uint32_t(maxLALAIdx), - // uint32_t(patternSize), - // uint32_t(img_height - patternSize * 2 - 14)); - - // std::cout << maxLALAIdx << ' '; // memset(correlation, 0, img_height * sizeof(correlation[0])); // memset(correlation, 0, patternSize); // memset(correlation + correlationSize - 1, 0, patternSize); @@ -93,29 +58,17 @@ float process_column(const Image::column_t &column) integralSum[i] = column[i] + integralSum[i - 1]; } - // sum_elapsed_ns += t.nsecsElapsed(); - // t.restart(); - // pixel * <sum of neighbours> for (uint32_t i = 0; i < correlationSize; ++i) // for (uint32_t i = maxLALAIdx; i < maxLALAIdx + patternSize * 2; ++i) correlation[i + patternSize / 2] = column[i + patternSize / 2] * (integralSum[i + patternOffset] - integralSum[i]); - // * (integralSum[i + patternSize] - integralSum[i]); - - // corr_elapsed_ns += t.nsecsElapsed(); - // t.restart(); uint32_t cPPP = correlation[0]; uint32_t cPP = correlation[1]; uint32_t cP = correlation[2]; - // uint32_t c = correlation[3]; - // uint32_t cN = correlation[4]; - // uint32_t cNN = correlation[5]; - for (uint32_t i = 3; i < img_height - 2; ++i) { - // for (uint32_t i = maxLALAIdx + 3; i < maxLALAIdx + patternSize * 3 - 2; ++i) { // p - pixel, n - neighbour // P - pixel used in sum, N - neighbour used in sum // [N P N] @@ -124,16 +77,6 @@ float process_column(const Image::column_t &column) const uint32_t cNN = correlation[i + 2]; const auto sum = cP + c + cN; - // const int32_t rioux0 = int32_t(cPPP + cPP) - int32_t(c + cN); - // const int32_t rioux1 = int32_t(cPP + cP) - int32_t(cN + cNN); - - // if (sum > maxTripleSum && rioux0 < 0 && rioux1 >= 0) { - // x1 = i - 1; - // y1 = rioux0; - // y2 = rioux1; - // maxTripleSum = sum; - // } - if (sum > maxTripleSum) { // [N N n p] - [P N] const int32_t rioux0 = int32_t(cPPP + cPP) - int32_t(c + cN); @@ -154,20 +97,71 @@ float process_column(const Image::column_t &column) cPPP = cPP; cPP = cP; cP = c; - // c = cN; - // cN = cNN; - // cNN = correlation[i + 1]; } - // value_elapsed_ns += t.nsecsElapsed(); - // t.restart(); - result = (y2 != y1) ? (float(x1) - (float(y1) / (y2 - y1))) : std::numeric_limits<float>::quiet_NaN(); return result; } +float process_column_center_of_mass(const Image::column_t &column) +{ + static_assert((img_height % patternSize) == 0, "img_height % patternSize should be 0"); + + // const auto &c = column; + Image::column_t c = column; + constexpr size_t win_size = 2; + constexpr size_t wins_count{img_height / win_size}; + using pixel_sum_t = uint16_t; + // using img_pixel_t = decltype(Image::row_t)::value_type; + using img_pixel_t = uint8_t; + static_assert(std::numeric_limits<pixel_sum_t>::max() + > std::numeric_limits<img_pixel_t>::max() * win_size, + "choose bigger type for pixels sum"); + // std::array<pixel_sum_t, wins_count> sums; + pixel_sum_t maxSum{0}; + size_t maxWinI{0}; + constexpr img_pixel_t blackLevel{5}; + + // std::for_each(c.begin(), c.end(), [](auto &v) { + // if (v > 20) { + // v = 0; + // } + // }); + + for (size_t w{0}; w < wins_count; ++w) { + // option 0: manually sum + const auto i = w * win_size; + // sums[w] = c[i + 0] * 1 + c[i + 1] * 2 + c[i + 2] * 3 + c[i + 3] * 4 + c[i + 4] * 5 + // + c[i + 5] * 6 + c[i + 6] * 7 + c[i + 7] * 8; + // const auto sum{c[i + 0] + c[i + 1] + c[i + 2] + c[i + 3] + c[i + 4] + c[i + 5] + c[i + 6] + // + c[i + 7]}; + const auto sum{c[i + 0] + c[i + 1] + c[i + 2] + c[i + 3]}; + + if (sum > maxSum) { + maxSum = sum; + maxWinI = w; + } + } + + const auto start = (maxWinI - 1) * win_size; + const auto com_size = win_size; + float com_sum{0}; + float com_sum_mass{0}; + constexpr uint8_t black_level{27}; + // std::array<uint8_t, win_size * 3> + + for (size_t i{start}; i < start + (win_size * 3); ++i) { + const auto value = c[i] > black_level ? c[i] : 0; + com_sum += value; + com_sum_mass += value * (i); + } + + // return com_sum / (win_size * 3); + return com_sum_mass / com_sum; +} + // uint8_t &Image::dataAt(size_t row, size_t col) // { // const auto index = img_width * row + col; @@ -206,12 +200,16 @@ std::shared_ptr<Pixels> Image::sharedPixels() result->counters = counters; std::transform(rotated_cw.cbegin(), rotated_cw.cend(), result->pixels.begin(), process_column); + // std::transform(rotated_cw.cbegin(), + // rotated_cw.cend(), + // result->pixels.begin(), + // process_column_center_of_mass); static bool found{false}; for (const auto &p : result->pixels) { - if (p > 400) { - std::cout << "AAAAAAAAAAAAAAAAAAAAA too big pixel values: " << p << std::endl; + if (p > img_height) { + // std::cout << "AAAAAAAAAAAAAAAAAAAAA too big pixel values: " << p << std::endl; found = true; } } diff --git a/src/iscanner.h b/src/iscanner.h index e09197e..e03aaa3 100644 --- a/src/iscanner.h +++ b/src/iscanner.h @@ -23,8 +23,8 @@ public: virtual void stopAllProtocols() = 0; // TODO: think about more flexible calibration interface - virtual CalibrationTablePtr calibrationTableX() const = 0; - virtual CalibrationTablePtr calibrationTableZ() const = 0; + virtual op::CalibrationTablePtr calibrationTableX() const = 0; + virtual op::CalibrationTablePtr calibrationTableZ() const = 0; // TODO: add nullptr check everywhere where this function is used std::shared_ptr<ICamera> camera() const; diff --git a/src/main.cpp b/src/main.cpp index 24e2bf4..2ca9bad 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -129,7 +129,7 @@ int main(int argc, char *argv[]) // return EXIT_SUCCESS; auto stand = std::make_shared<Esp32Stand>(QHostAddress{"192.168.18.248"}, 80, 1600); - if (false) { + if (true) { if (false) { // const QString tmpFN{"/home/radxa/dumps/binz/raw_profile_meas_100009_enc_35416"}; // const QString tmpFN{"/home/radxa/dumps/binz/raw_profile_meas_754290_enc_276336"}; @@ -151,42 +151,35 @@ int main(int argc, char *argv[]) exit(EXIT_FAILURE); } - // open binary calibration table - if (false) { + // open binary calibration tables + if (true) { initializers << QtConcurrent::run([&]() { - if (!openCalibrationTable(QStringLiteral("/home/%1/dumps/binz.calibration_table") - .arg(user), - // "/tmp/binz.calibration_table", - scanner->calibrationTableZ())) { + if (!op::openCalibrationTable(QStringLiteral( + "/home/%1/dumps/binz.calibration_table") + .arg(user), + // "/tmp/binz.calibration_table", + scanner->calibrationTableZ())) { exit(EXIT_FAILURE); } }); initializers << QtConcurrent::run([&]() { - if (!openCalibrationTable(QStringLiteral("/home/%1/dumps/binx.calibration_table") - .arg(user), - // "/tmp/binx.calibration_table", - scanner->calibrationTableX())) { + if (!op::openCalibrationTable(QStringLiteral( + "/home/%1/dumps/binx.calibration_table") + .arg(user), + // "/tmp/binx.calibration_table", + scanner->calibrationTableX())) { exit(EXIT_FAILURE); } }); } - if (false) { - auto rawProfiles = openDump(QStringLiteral("/home/%1/dumps/binx").arg(user)); - qDebug() << "raw x-profiles count is" << rawProfiles.size(); - // qDebug() << "height" << calibrationColumnHeight; - - auto filteredRawProfiles = filter(std::move(rawProfiles)); - qDebug() << "filtered x-profiles count is" << filteredRawProfiles.count(); - - calibrateX(std::move(filteredRawProfiles), scanner->calibrationTableX()); - - interpolate(scanner->calibrationTableX()); + for (auto &i : initializers) { + i.waitForFinished(); } // load binary calibration dumps and calibrate - if (true) { + if (false) { if (true) { auto rawProfiles = openDump(QStringLiteral("/home/%1/dumps/binz").arg(user)); qDebug() << "raw z-profiles count is" << rawProfiles.size(); @@ -195,39 +188,28 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } - // for (const auto &rp : rawProfiles) { - // if (rp.debugPrintIfInvalid()) { - // exit(EXIT_FAILURE); - // } - // } - - exit(EXIT_FAILURE); + qDebug() << "filtered z-profiles count is" << rawProfiles.count(); - std::sort(rawProfiles.begin(), rawProfiles.end(), [](const auto &a, const auto &b) { - return a.counters.encoderPosition < b.counters.encoderPosition; - }); - auto filteredRawProfiles = filter(std::move(rawProfiles)); - qDebug() << "filtered z-profiles count is" << filteredRawProfiles.count(); - - if (!calibrateZ(std::move(filteredRawProfiles), - // requested_params.stepsPerMm, - stand->stepsPerMm(), - scanner->calibrationTableZ())) { + if (!op::calibrateZ(std::move(rawProfiles), + stand->stepsPerMm(), + scanner->calibrationTableZ())) { qCritical() << "Z calibration failed, exiting"; exit(EXIT_FAILURE); } - // DEBUG - - // interpolate(scanner->calibrationTableZ()); const auto dumpZPath = QStringLiteral("/home/%1/dumps/binz.calibration_table") .arg(user); - if (!dump(scanner->calibrationTableZ(), dumpZPath)) { + op::calibrationTableToImage(scanner->calibrationTableZ()).save(dumpZPath + ".png"); + + op::interpolate(scanner->calibrationTableZ()); + + if (!op::dump(scanner->calibrationTableZ(), dumpZPath)) { qApp->exit(EXIT_FAILURE); } qDebug() << __func__ << "calibration table z dump finished"; - calibrationTableToImage(scanner->calibrationTableZ()).save(dumpZPath + ".png"); + op::calibrationTableToImage(scanner->calibrationTableZ()) + .save(dumpZPath + "_interpolated.png"); qDebug() << __func__ << "calibration table z image dump finished"; } @@ -238,27 +220,29 @@ int main(int argc, char *argv[]) auto rawProfiles = openDump(QStringLiteral("/home/%1/dumps/binx").arg(user)); qDebug() << "raw x-profiles count is" << rawProfiles.size(); - auto filteredRawProfiles = filter(std::move(rawProfiles)); - qDebug() << "filtered x-profiles count is" << filteredRawProfiles.count(); + // auto filteredRawProfiles = op::filter(std::move(rawProfiles)); + // qDebug() << "filtered x-profiles count is" << filteredRawProfiles.count(); - if (!calibrateX(std::move(filteredRawProfiles), scanner->calibrationTableX())) { + // if (!op::calibrateX(std::move(filteredRawProfiles), scanner->calibrationTableX())) { + if (!op::calibrateX(std::move(rawProfiles), scanner->calibrationTableX())) { qCritical() << "X calibration failed, exiting"; exit(EXIT_FAILURE); } - qDebug() << __func__ << "try to interpolate"; - interpolate(scanner->calibrationTableX()); - qDebug() << __func__ << "interpolated"; - const auto dumpXPath = QStringLiteral("/home/%1/dumps/binx.calibration_table") .arg(user); + op::calibrationTableToImage(scanner->calibrationTableZ()).save(dumpXPath + ".png"); + qDebug() << __func__ << "try to interpolate"; + op::interpolate(scanner->calibrationTableX()); + qDebug() << __func__ << "interpolated"; - if (!dump(scanner->calibrationTableX(), dumpXPath)) { + if (!op::dump(scanner->calibrationTableX(), dumpXPath)) { qCritical() << "cannot dump calibration table x, exit with failure"; qApp->exit(EXIT_FAILURE); } qDebug() << __func__ << "calibration table x dump finished"; - calibrationTableToImage(scanner->calibrationTableZ()).save(dumpXPath + ".png"); + op::calibrationTableToImage(scanner->calibrationTableZ()) + .save(dumpXPath + "_interpolated.png"); qDebug() << __func__ << "calibration table x image dump finished"; } } diff --git a/src/pixels.cpp b/src/pixels.cpp index 5b7b226..3183257 100644 --- a/src/pixels.cpp +++ b/src/pixels.cpp @@ -120,17 +120,18 @@ Pixels::operator bool() const bool Pixels::debugPrintIfInvalid() const { - const bool valid{std::find_if(std::execution::par_unseq, - pixels.cbegin(), - pixels.cend(), - [](const auto &p) { return p > 280; }) - == pixels.cend()}; + // const bool valid{std::find_if(std::execution::par_unseq, + // pixels.cbegin(), + // pixels.cend(), + // [](const auto &p) { return p > 280; }) + // == pixels.cend()}; - if (!valid) { - qCritical() << Q_FUNC_INFO << "got invalid profile:" << *this; - } + // if (!valid) { + // qCritical() << Q_FUNC_INFO << "got invalid profile:" << *this; + // } - return valid; + // return valid; + return true; } // std::lock_guard<std::mutex> Pixels::lock() diff --git a/src/profile.cpp b/src/profile.cpp index 101ecf3..9a65730 100644 --- a/src/profile.cpp +++ b/src/profile.cpp @@ -5,10 +5,9 @@ #include <QDebug> #include <QJsonArray> -Profile::Profile( - const Pixels& pixels, - const CalibrationTablePtr calibrationTableZ, - const CalibrationTablePtr calibrationTableX) +Profile::Profile(const Pixels &pixels, + const op::CalibrationTablePtr calibrationTableZ, + const op::CalibrationTablePtr calibrationTableX) : m_counters(pixels.counters) { // if (!calibrationTableZ || !calibrationTableX) { @@ -41,7 +40,7 @@ Profile::Profile( qt_noop(); } const auto pixelDiscrete = pixel * discretesInRage / img_height; - if (Q_UNLIKELY(pixel >= sizeof(CalibrationColumn) / sizeof(float))) { + if (Q_UNLIKELY(pixel >= sizeof(op::CalibrationColumn) / sizeof(float))) { qWarning() << "got invalid calibration pixel at" << i << ":" << pixel; m_counters = {}; @@ -49,7 +48,7 @@ Profile::Profile( return; } - if (Q_UNLIKELY(pixel == sizeof(CalibrationColumn) - 1)) { + if (Q_UNLIKELY(pixel == sizeof(op::CalibrationColumn) - 1)) { qDebug() << "Profile: got edge value"; const auto& z = calibrationTableZ->at(i).at(pixelDiscrete); @@ -86,9 +85,9 @@ Profile::Profile( continue; } - // const auto x = (leftMmX * (1 - fract) + rightMmX * fract); + const auto x = (leftMmX * (1 - fract) + rightMmX * fract); // TODO: revert this debug shit - const auto x = static_cast<double>(i) - img_width / 2; + // const auto x = static_cast<double>(i) - img_width / 2; m_pointsMm.at(i) = {x, z}; diff --git a/src/profile.h b/src/profile.h index 0e2f839..6a4d6f3 100644 --- a/src/profile.h +++ b/src/profile.h @@ -13,9 +13,9 @@ public: public: // TODO: make private/protected - explicit Profile(const Pixels& pixels, - const CalibrationTablePtr calibrationTableZ, - const CalibrationTablePtr calibrationTableX); + explicit Profile(const Pixels &pixels, + const op::CalibrationTablePtr calibrationTableZ, + const op::CalibrationTablePtr calibrationTableX); public: const Counters& counters() const; diff --git a/src/protocols/httpserver.cpp b/src/protocols/httpserver.cpp index e5772da..b20ee45 100644 --- a/src/protocols/httpserver.cpp +++ b/src/protocols/httpserver.cpp @@ -233,6 +233,8 @@ QHttpServerResponse HttpServer::POST_params(const QHttpServerRequest &request) const auto autoGain = json["autoGain"]; const auto gain = json["gain"]; const auto triggerExposureDelayUs = json["triggerExposureDelayUs"]; + const auto blackLevel = json["blackLevel"]; + const auto isBlackLevelManual = json["isBlackLevelManual"]; // TODO: unify args processing to avoid code duplicates if (!autoExposure.isNull()) { @@ -278,17 +280,47 @@ QHttpServerResponse HttpServer::POST_params(const QHttpServerRequest &request) std::chrono::microseconds{triggerExposureDelayUs.toInt()}); if (!ok) { + qCritical() << "cannot write delay value"; { invalidValue("triggerExposureDelayUs"), QHttpServerResponse::StatusCode::BadRequest; } } + + const auto delay = m_scanner->camera()->get_triggerExposureDelay(); + + if (delay.has_value()) { + std::cout << "NEW DELAY IS: " << *delay << std::endl; + } else { + qCritical() << "cannot read new delay value"; + } } else { return {invalidValue("triggerExposureDelayUs"), QHttpServerResponse::StatusCode::BadRequest}; } } + if (!isBlackLevelManual.isNull()) { + if (isBlackLevelManual.isBool()) { + if (!m_scanner->camera()->set_isBlackLevelManual(isBlackLevelManual.toBool())) { + return {invalidValue("isBlackLevelManualisBlackLevelManual"), + QHttpServerResponse::StatusCode::BadRequest}; + } + } else { + return {invalidValue("isBlackLevelManual"), QHttpServerResponse::StatusCode::BadRequest}; + } + } + + if (!blackLevel.isNull()) { + if (blackLevel.isDouble()) { + if (!m_scanner->camera()->set_blackLevel(blackLevel.toInteger())) { + return {invalidValue("blackLevel"), QHttpServerResponse::StatusCode::BadRequest}; + } + } else { + return {invalidValue("blackLevel"), QHttpServerResponse::StatusCode::BadRequest}; + } + } + return QHttpServerResponse::StatusCode::Ok; } @@ -308,6 +340,8 @@ QHttpServerResponse HttpServer::GET_params() {"exposureTime", chrono_val_or_null(m_scanner->camera()->get_exposureTime())}, {"autoGain", val_or_null(m_scanner->camera()->get_autoGain())}, {"gain", val_or_null(m_scanner->camera()->get_gain())}, + {"blackLevel", static_cast<qint64>(m_scanner->camera()->get_blackLevel().value_or(0))}, + {"isBlackLevelManual", val_or_null(m_scanner->camera()->get_isBlackLevelManual())}, }; qDebug().noquote() << __func__ << ": " << json; diff --git a/src/scanner.cpp b/src/scanner.cpp index 4045121..d2446d4 100644 --- a/src/scanner.cpp +++ b/src/scanner.cpp @@ -4,12 +4,12 @@ Scanner::Scanner(std::shared_ptr<ICamera> camera, std::vector<std::shared_ptr<IProtocol>> protocols) : IScanner{camera, protocols} - , m_calibrationTableX{new CalibrationTable{}} - , m_calibrationTableZ{new CalibrationTable{}} + , m_calibrationTableX{new op::CalibrationTable{}} + , m_calibrationTableZ{new op::CalibrationTable{}} { // m_protocols.push_back() - memset(m_calibrationTableX.get(), 0, sizeof(CalibrationTable)); - memset(m_calibrationTableZ.get(), 0, sizeof(CalibrationTable)); + memset(m_calibrationTableX.get(), 0, sizeof(op::CalibrationTable)); + memset(m_calibrationTableZ.get(), 0, sizeof(op::CalibrationTable)); } bool Scanner::startAllProtocols() @@ -32,12 +32,12 @@ void Scanner::stopAllProtocols() } } -CalibrationTablePtr Scanner::calibrationTableX() const +op::CalibrationTablePtr Scanner::calibrationTableX() const { return m_calibrationTableX; } -CalibrationTablePtr Scanner::calibrationTableZ() const +op::CalibrationTablePtr Scanner::calibrationTableZ() const { return m_calibrationTableZ; } diff --git a/src/scanner.h b/src/scanner.h index 4d81d1a..6f7cbfa 100644 --- a/src/scanner.h +++ b/src/scanner.h @@ -13,10 +13,10 @@ public: bool startAllProtocols() override; void stopAllProtocols() override; - CalibrationTablePtr calibrationTableX() const override; - CalibrationTablePtr calibrationTableZ() const override; + op::CalibrationTablePtr calibrationTableX() const override; + op::CalibrationTablePtr calibrationTableZ() const override; private: - CalibrationTablePtr m_calibrationTableX; - CalibrationTablePtr m_calibrationTableZ; + op::CalibrationTablePtr m_calibrationTableX; + op::CalibrationTablePtr m_calibrationTableZ; }; |
