<commit_before>//*****************************************************************************
// Copyright 2017-2019 Intel Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//*****************************************************************************

#ifdef _WIN32
#include <windows.h>
#else
#include <dlfcn.h>
#endif

#include <sstream>

#include "ngraph/file_util.hpp"
#include "ngraph/runtime/backend.hpp"
#include "ngraph/runtime/backend_manager.hpp"
#include "ngraph/runtime/interpreter/static_initialize.hpp"
#include "ngraph/util.hpp"

using namespace std;
using namespace ngraph;

#ifdef _WIN32
#define CLOSE_LIBRARY(a) FreeLibrary(a)
#define DLSYM(a, b) GetProcAddress(a, b)
#else
#define CLOSE_LIBRARY(a) dlclose(a)
#define DLSYM(a, b) dlsym(a, b)
#endif

unordered_map<string, runtime::BackendConstructor*>& runtime::BackendManager::get_registry()
{
    static unordered_map<string, BackendConstructor*> s_registered_backend;
    static bool s_static_initialization_called = false;
    if (!s_static_initialization_called)
    {
        s_static_initialization_called = true;
#ifdef INTERPRETER_BACKEND_STATIC
        runtime::interpreter::static_initialize();
#endif
    }
    return s_registered_backend;
}

void runtime::BackendManager::register_backend(const string& name, BackendConstructor* new_backend)
{
    get_registry()[name] = new_backend;
}

vector<string> runtime::BackendManager::get_registered_backends()
{
    vector<string> rc;
    for (const auto& p : get_registry())
    {
        rc.push_back(p.first);
    }
    for (const auto& p : get_registered_device_map())
    {
        if (find(rc.begin(), rc.end(), p.first) == rc.end())
        {
            rc.push_back(p.first);
        }
    }
    return rc;
}

shared_ptr<runtime::Backend> runtime::BackendManager::create_backend(const std::string& config)
{
    shared_ptr<runtime::Backend> backend;
    string type = config;

    // strip off attributes, IE:CPU becomes IE
    auto colon = type.find(":");
    if (colon != type.npos)
    {
        type = type.substr(0, colon);
    }

    auto registry = get_registry();
    auto it = registry.find(type);
    for (auto x : registry)
    {
        NGRAPH_INFO << x.first;
    }
    if (it != registry.end())
    {
        BackendConstructor* new_backend = it->second;
        backend = new_backend->create(config);
    }
    else
    {
        DL_HANDLE handle = open_shared_library(type);
        if (!handle)
        {
            stringstream ss;
            ss << "Backend '" << type << "' not registered. Error:";
#ifndef _WIN32
            ss << dlerror();
#endif
            throw runtime_error(ss.str());
        }

#ifndef _WIN32
        dlerror(); // Clear any pending errors
#endif
        function<runtime::BackendConstructor*()> get_backend_constructor_pointer =
            reinterpret_cast<runtime::BackendConstructor* (*)()>(
                DLSYM(handle, "get_backend_constructor_pointer"));
        if (get_backend_constructor_pointer)
        {
            backend = get_backend_constructor_pointer()->create(config);
        }
        else
        {
            string error;
#ifndef _WIN32
            const char* err = dlerror();
            error = (err ? err : "");
#endif
            CLOSE_LIBRARY(handle);
            throw runtime_error(
                "Failed to find symbol 'get_backend_constructor_pointer' in backend "
                "library.\nError='" +
                error + "'");
        }
    }
    return backend;
}

// This doodad finds the full path of the containing shared library
static string find_my_file()
{
#ifdef _WIN32
    HMODULE hModule = GetModuleHandleW(L"ngraph.dll");
    WCHAR wpath[MAX_PATH];
    GetModuleFileNameW(hModule, wpath, MAX_PATH);
    wstring ws(wpath);
    string path(ws.begin(), ws.end());
    replace(path.begin(), path.end(), '\\', '/');
    path = file_util::get_directory(path);
    path += "/";
    return path;
#else
    Dl_info dl_info;
    dladdr(reinterpret_cast<void*>(find_my_file), &dl_info);
    return dl_info.dli_fname;
#endif
}

DL_HANDLE runtime::BackendManager::open_shared_library(string type)
{
    string lib_prefix = SHARED_LIB_PREFIX;
    string lib_suffix = SHARED_LIB_SUFFIX;

    DL_HANDLE handle = nullptr;

    // strip off attributes, IE:CPU becomes IE
    auto colon = type.find(":");
    if (colon != type.npos)
    {
        type = type.substr(0, colon);
    }

    string library_name = lib_prefix + to_lower(type) + "_backend" + lib_suffix;
    string my_directory = file_util::get_directory(find_my_file());
    string library_path = file_util::path_join(my_directory, library_name);
    string error;
#ifdef _WIN32
    SetDllDirectory((LPCSTR)my_directory.c_str());
    handle = LoadLibrary(library_path.c_str());
#else
    dlerror(); // Clear any pending errors
    handle = dlopen(library_path.c_str(), RTLD_NOW | RTLD_GLOBAL);
    const char* err = dlerror();
    error = (err ? err : "");
#endif
    if (!handle)
    {
        stringstream ss;
        ss << "Unable to find backend '" << type << "' as file '" << library_path << "'";
        ss << "\nOpen error message '" << error << "'";
        throw runtime_error(ss.str());
    }
    return handle;
}

map<string, string> runtime::BackendManager::get_registered_device_map()
{
    map<string, string> rc;
    string my_directory = file_util::get_directory(find_my_file());
    vector<string> backend_list;

    auto f = [&](const string& file, bool is_dir) {
        if (!is_dir)
        {
            string name = file_util::get_file_name(file);
            string backend_name;
            if (is_backend_name(name, backend_name))
            {
                rc.insert({to_upper(backend_name), file});
            }
        }
    };
    file_util::iterate_files(my_directory, f, false, true);
    return rc;
}

bool runtime::BackendManager::is_backend_name(const string& file, string& backend_name)
{
    bool rc = false;
    string name = file_util::get_file_name(file);
    string lib_prefix = SHARED_LIB_PREFIX;
    string lib_suffix = SHARED_LIB_SUFFIX;
    if ((name.size() > lib_prefix.size() + lib_suffix.size()) &
        !name.compare(0, lib_prefix.size(), lib_prefix))
    {
        if (!name.compare(name.size() - lib_suffix.size(), lib_suffix.size(), lib_suffix))
        {
            auto pos = name.find("_backend");
            if (pos != name.npos)
            {
                backend_name = name.substr(lib_prefix.size(), pos - lib_prefix.size());
                rc = true;
            }
        }
    }
    return rc;
}
<commit_msg>optimization for creating backend<commit_after>//*****************************************************************************
// Copyright 2017-2019 Intel Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//*****************************************************************************

#ifdef _WIN32
#include <windows.h>
#else
#include <dlfcn.h>
#endif

#include <sstream>

#include "ngraph/file_util.hpp"
#include "ngraph/runtime/backend.hpp"
#include "ngraph/runtime/backend_manager.hpp"
#include "ngraph/runtime/interpreter/static_initialize.hpp"
#include "ngraph/util.hpp"

using namespace std;
using namespace ngraph;

#ifdef _WIN32
#define CLOSE_LIBRARY(a) FreeLibrary(a)
#define DLSYM(a, b) GetProcAddress(a, b)
#else
#define CLOSE_LIBRARY(a) dlclose(a)
#define DLSYM(a, b) dlsym(a, b)
#endif

unordered_map<string, runtime::BackendConstructor*>& runtime::BackendManager::get_registry()
{
    static unordered_map<string, BackendConstructor*> s_registered_backend;
    static bool s_static_initialization_called = false;
    if (!s_static_initialization_called)
    {
        s_static_initialization_called = true;
#ifdef INTERPRETER_BACKEND_STATIC
        runtime::interpreter::static_initialize();
#endif
    }
    return s_registered_backend;
}

void runtime::BackendManager::register_backend(const string& name, BackendConstructor* new_backend)
{
    get_registry()[name] = new_backend;
}

vector<string> runtime::BackendManager::get_registered_backends()
{
    vector<string> rc;
    for (const auto& p : get_registry())
    {
        rc.push_back(p.first);
    }
    for (const auto& p : get_registered_device_map())
    {
        if (find(rc.begin(), rc.end(), p.first) == rc.end())
        {
            rc.push_back(p.first);
        }
    }
    return rc;
}

shared_ptr<runtime::Backend> runtime::BackendManager::create_backend(const std::string& config)
{
    shared_ptr<runtime::Backend> backend;
    string type = config;

    // strip off attributes, IE:CPU becomes IE
    auto colon = type.find(":");
    if (colon != type.npos)
    {
        type = type.substr(0, colon);
    }

    auto registry = get_registry();
    auto it = registry.find(type);
    // for (auto x : registry)
    // {
    //     NGRAPH_INFO << x.first;
    // }
    if (it != registry.end())
    {
        BackendConstructor* new_backend = it->second;
        backend = new_backend->create(config);
    }
    else
    {
        // NGRAPH_INFO << "open";
        DL_HANDLE handle = open_shared_library(type);
        if (!handle)
        {
            stringstream ss;
            ss << "Backend '" << type << "' not registered. Error:";
#ifndef _WIN32
            ss << dlerror();
#endif
            throw runtime_error(ss.str());
        }

#ifndef _WIN32
        dlerror(); // Clear any pending errors
#endif
        function<runtime::BackendConstructor*()> get_backend_constructor_pointer =
            reinterpret_cast<runtime::BackendConstructor* (*)()>(
                DLSYM(handle, "get_backend_constructor_pointer"));
        if (get_backend_constructor_pointer)
        {
            backend = get_backend_constructor_pointer()->create(config);
            register_backend(type, get_backend_constructor_pointer());
        }
        else
        {
            string error;
#ifndef _WIN32
            const char* err = dlerror();
            error = (err ? err : "");
#endif
            CLOSE_LIBRARY(handle);
            throw runtime_error(
                "Failed to find symbol 'get_backend_constructor_pointer' in backend "
                "library.\nError='" +
                error + "'");
        }
    }
    return backend;
}

// This doodad finds the full path of the containing shared library
static string find_my_file()
{
#ifdef _WIN32
    HMODULE hModule = GetModuleHandleW(L"ngraph.dll");
    WCHAR wpath[MAX_PATH];
    GetModuleFileNameW(hModule, wpath, MAX_PATH);
    wstring ws(wpath);
    string path(ws.begin(), ws.end());
    replace(path.begin(), path.end(), '\\', '/');
    path = file_util::get_directory(path);
    path += "/";
    return path;
#else
    Dl_info dl_info;
    dladdr(reinterpret_cast<void*>(find_my_file), &dl_info);
    return dl_info.dli_fname;
#endif
}

DL_HANDLE runtime::BackendManager::open_shared_library(string type)
{
    string lib_prefix = SHARED_LIB_PREFIX;
    string lib_suffix = SHARED_LIB_SUFFIX;

    DL_HANDLE handle = nullptr;

    // strip off attributes, IE:CPU becomes IE
    auto colon = type.find(":");
    if (colon != type.npos)
    {
        type = type.substr(0, colon);
    }

    string library_name = lib_prefix + to_lower(type) + "_backend" + lib_suffix;
    string my_directory = file_util::get_directory(find_my_file());
    string library_path = file_util::path_join(my_directory, library_name);
    string error;
#ifdef _WIN32
    SetDllDirectory((LPCSTR)my_directory.c_str());
    handle = LoadLibrary(library_path.c_str());
#else
    dlerror(); // Clear any pending errors
    handle = dlopen(library_path.c_str(), RTLD_NOW | RTLD_GLOBAL);
    const char* err = dlerror();
    error = (err ? err : "");
#endif
    if (!handle)
    {
        stringstream ss;
        ss << "Unable to find backend '" << type << "' as file '" << library_path << "'";
        ss << "\nOpen error message '" << error << "'";
        throw runtime_error(ss.str());
    }
    return handle;
}

map<string, string> runtime::BackendManager::get_registered_device_map()
{
    map<string, string> rc;
    string my_directory = file_util::get_directory(find_my_file());
    vector<string> backend_list;

    auto f = [&](const string& file, bool is_dir) {
        if (!is_dir)
        {
            string name = file_util::get_file_name(file);
            string backend_name;
            if (is_backend_name(name, backend_name))
            {
                rc.insert({to_upper(backend_name), file});
            }
        }
    };
    file_util::iterate_files(my_directory, f, false, true);
    return rc;
}

bool runtime::BackendManager::is_backend_name(const string& file, string& backend_name)
{
    bool rc = false;
    string name = file_util::get_file_name(file);
    string lib_prefix = SHARED_LIB_PREFIX;
    string lib_suffix = SHARED_LIB_SUFFIX;
    if ((name.size() > lib_prefix.size() + lib_suffix.size()) &
        !name.compare(0, lib_prefix.size(), lib_prefix))
    {
        if (!name.compare(name.size() - lib_suffix.size(), lib_suffix.size(), lib_suffix))
        {
            auto pos = name.find("_backend");
            if (pos != name.npos)
            {
                backend_name = name.substr(lib_prefix.size(), pos - lib_prefix.size());
                rc = true;
            }
        }
    }
    return rc;
}
<|endoftext|>


<commit_before>/*******************************************************************************
 * This file is part of "Patrick's Programming Library", Version 7 (PPL7).
 * Web: http://www.pfp.de/ppl/
 *
 *******************************************************************************
 * Copyright (c) 2022, Patrick Fedick <patrick@pfp.de>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *    1. Redistributions of source code must retain the above copyright notice, this
 *       list of conditions and the following disclaimer.
 *    2. Redistributions in binary form must reproduce the above copyright notice,
 *       this list of conditions and the following disclaimer in the documentation
 *       and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER AND CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 *******************************************************************************/

#include "prolog_ppl7.h"

#ifdef HAVE_STDIO_H
#include <stdio.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_STDARG_H
#include <stdarg.h>
#endif

#include "ppl7.h"
#include "ppl7-grafix.h"
#include "ppl7-tk.h"

namespace ppl7::tk {

AbstractSpinBox::AbstractSpinBox()
{
    up_button=NULL;
    down_button=NULL;
    text_input=NULL;
    createUi();
}

AbstractSpinBox::~AbstractSpinBox()
{
    if (text_input) delete text_input;
    if (up_button) delete up_button;
    if (down_button) delete down_button;
}

AbstractSpinBox::AbstractSpinBox(int x, int y, int width, int height, const String& text)
{
    create(x, y, width, height);
    up_button=NULL;
    down_button=NULL;
    text_input=NULL;
    createUi();
    resizeUi();
    text_input->setText(text);
}

void AbstractSpinBox::createUi()
{
    Grafix* gfx=GetGrafix();
    text_input=new LineInput();
    text_input->setEventHandler(this);
    addChild(text_input);
    up_button=new Button();
    up_button->setEventHandler(this);
    addChild(up_button);
    down_button=new Button();
    down_button->setEventHandler(this);
    addChild(down_button);
    if (gfx) {
        const WidgetStyle& style=GetWidgetStyle();
        img_up=gfx->ButtonSymbolsSmall.getDrawable(3, style.buttonSymbolColor);
        img_down=gfx->ButtonSymbolsSmall.getDrawable(4, style.buttonSymbolColor);
        up_button->setIcon(img_up);
        down_button->setIcon(img_down);
    }
}

void AbstractSpinBox::resizeUi()
{
    Rect client=this->clientRect();
    text_input->setSize(client.width() - 25, client.height());
    text_input->setPos(client.left(), client.top());

    up_button->setSize(25, client.height() / 2);
    up_button->setPos(client.width() - 25, client.top());

    down_button->setSize(25, client.height() / 2);
    down_button->setPos(client.width() - 25, client.top() + client.height() / 2);
}

const Color& AbstractSpinBox::color() const
{
    return text_input->color();
}

void AbstractSpinBox::setColor(const Color& c)
{
    text_input->setColor(c);
}

const Font& AbstractSpinBox::font() const
{
    return text_input->font();
}

void AbstractSpinBox::setFont(const Font& font)
{
    text_input->setFont(font);
}

void AbstractSpinBox::setText(const String& value)
{
    text_input->setText(value);
    Event new_event(Event::TextChanged);
    new_event.setWidget(this);
    EventHandler::textChangedEvent(&new_event, value);
}

String AbstractSpinBox::text() const
{
    return String(text_input->text());
}

String AbstractSpinBox::widgetType() const
{
    return "AbstractSpinBox";
}

void AbstractSpinBox::debugEvent(const ppl7::String& name, Event* event)
{
    return;
    Widget* w=event->widget();
    ppl7::String wname="unknown";
    if (w == this) wname="AbstractSpinBox";
    else if (w == up_button) wname="UpButton";
    else if (w == down_button) wname="DownButton";
    else if (w == text_input) wname="TextInput";

    printf("Event [%s]: %s\n", (const char*)wname, (const char*)name);

}

void AbstractSpinBox::setInputValidator(InputValidator* validator)
{
    text_input->setInputValidator(validator);
}

void AbstractSpinBox::paint(Drawable& draw)
{

}

void AbstractSpinBox::mouseDownEvent(MouseEvent* event)
{
    debugEvent("AbstractSpinBox::mouseDownEvent", event);
    Widget* w=event->widget();
    if (w == up_button) stepUp();
    else if (w == down_button) stepDown();
    GetWindowManager()->setKeyboardFocus(text_input);

}

void AbstractSpinBox::gotFocusEvent(FocusEvent* event)
{
    debugEvent("AbstractSpinBox::gotFocusEvent", event);
    GetWindowManager()->setKeyboardFocus(text_input);
}

void AbstractSpinBox::lostFocusEvent(FocusEvent* event)
{
    debugEvent("AbstractSpinBox::lostFocusEvent", event);
}

void AbstractSpinBox::textInputEvent(TextInputEvent* event)
{
    debugEvent("AbstractSpinBox::textInputEvent", event);
}

void AbstractSpinBox::keyDownEvent(KeyEvent* event)
{
    debugEvent("AbstractSpinBox::keyDownEvent", event);
    KeyEvent new_event=*event;
    new_event.setWidget(this);
    EventHandler::keyDownEvent(&new_event);
}

void AbstractSpinBox::keyUpEvent(KeyEvent* event)
{
    debugEvent("AbstractSpinBox::keyUpEvent", event);
}

void AbstractSpinBox::textChangedEvent(Event* event, const String& text)
{
    debugEvent("AbstractSpinBox::textChangedEvent", event);
    Event new_event=*event;
    new_event.setWidget(this);
    EventHandler::textChangedEvent(&new_event, text);
}

SpinBox::SpinBox()
{
    my_value=0;
    step_size=1;
    min=0;
    max=0;
    setInputValidator(this);
}

SpinBox::SpinBox(int x, int y, int width, int height, int64_t value)
    :AbstractSpinBox(x, y, width, height)
{
    my_value=0;
    step_size=1;
    min=0;
    max=0;
    setInputValidator(this);
    if (value < min) min=value;
    if (value > max) max=value;
    setValue(value);
}

String SpinBox::widgetType() const
{
    return "SpinBox";
}

void SpinBox::setValue(int64_t value)
{
    if (value<min || value>max) return;
    my_value=value;
    setText(ppl7::ToString("%ld", value));
}

int64_t SpinBox::value() const
{
    return text().toInt64();
}

void SpinBox::setMinimum(int64_t value)
{
    min=value;
    if (my_value < min) my_value=min;
}

void SpinBox::setMaximum(int64_t value)
{
    max=value;
    if (my_value > max) my_value=max;
}

void SpinBox::setLimits(int64_t min, int64_t max)
{
    setMinimum(min);
    setMaximum(max);
}

int64_t SpinBox::minimum() const
{
    return min;
}

int64_t SpinBox::maximum() const
{
    return max;
}

void SpinBox::setStepSize(int64_t value)
{
    step_size=value;
}

int64_t SpinBox::stepSize() const
{
    return step_size;
}

void SpinBox::stepUp()
{
    int64_t v=value() + step_size;
    if (v > max) v=max;
    setValue(v);
}

void SpinBox::stepDown()
{
    int64_t v=value() - step_size;
    if (v < min) v=min;
    setValue(v);
}

bool SpinBox::validateText(const ppl7::WideString& text)
{
    ppl7::String t=text;
    if (t.isEmpty() || t.pregMatch("/^-?[0-9]*$/")) {
        int64_t v=text.toInt64();
        if (v >= min && v <= max) return true;
        return false;
    }
    return false;
}

DoubleSpinBox::DoubleSpinBox()
{
    my_value=0.0f;
    my_decimals=2;
    step_size=1;
    min=0;
    max=0;
    setInputValidator(this);
}

DoubleSpinBox::DoubleSpinBox(int x, int y, int width, int height, double value, int decimals)
    :AbstractSpinBox(x, y, width, height)
{
    my_decimals=decimals;
    step_size=1;
    min=0;
    max=0;
    setInputValidator(this);
    if (value < min) min=value;
    if (value > max) max=value;
    setValue(value);
}

String DoubleSpinBox::widgetType() const
{
    return "DoubleSpinBox";
}

void DoubleSpinBox::setValue(double value)
{
    if (value<min || value>max) return;
    my_value=value;
    ppl7::String format;
    format.setf("%%0.%df", my_decimals);
    setText(ppl7::ToString((const char*)format, value));

}

double DoubleSpinBox::value() const
{
    return text().toDouble();
}

void DoubleSpinBox::setMinimum(double value)
{
    min=value;
    if (my_value < min) my_value=min;
}

void DoubleSpinBox::setMaximum(double value)
{
    max=value;
    if (my_value > max) my_value=max;
}

void DoubleSpinBox::setLimits(double min, double max)
{
    setMinimum(min);
    setMaximum(max);
}

double DoubleSpinBox::minimum() const
{
    return min;
}

double DoubleSpinBox::maximum() const
{
    return max;
}

void DoubleSpinBox::setStepSize(double value)
{
    step_size=value;
}

double DoubleSpinBox::stepSize() const
{
    return step_size;
}

void DoubleSpinBox::stepUp()
{
    int64_t v=value() + step_size;
    if (v > max) v=max;
    setValue(v);
}

void DoubleSpinBox::stepDown()
{
    int64_t v=value() - step_size;
    if (v < min) v=min;
    setValue(v);
}

bool DoubleSpinBox::validateText(const ppl7::WideString& text)
{
    ppl7::String t=text;
    if (t.isEmpty() || t.pregMatch("/^-?[0-9\\.,]*$/")) {
        double v=text.toDouble();
        if (v >= min && v <= max) return true;
    }
    return false;
}

}   // EOF namespace
<commit_msg>added methods to read and set background color on SpinBox<commit_after>/*******************************************************************************
 * This file is part of "Patrick's Programming Library", Version 7 (PPL7).
 * Web: http://www.pfp.de/ppl/
 *
 *******************************************************************************
 * Copyright (c) 2022, Patrick Fedick <patrick@pfp.de>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *    1. Redistributions of source code must retain the above copyright notice, this
 *       list of conditions and the following disclaimer.
 *    2. Redistributions in binary form must reproduce the above copyright notice,
 *       this list of conditions and the following disclaimer in the documentation
 *       and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER AND CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 *******************************************************************************/

#include "prolog_ppl7.h"

#ifdef HAVE_STDIO_H
#include <stdio.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_STDARG_H
#include <stdarg.h>
#endif

#include "ppl7.h"
#include "ppl7-grafix.h"
#include "ppl7-tk.h"

namespace ppl7::tk {

AbstractSpinBox::AbstractSpinBox()
{
    up_button=NULL;
    down_button=NULL;
    text_input=NULL;
    createUi();
}

AbstractSpinBox::~AbstractSpinBox()
{
    if (text_input) delete text_input;
    if (up_button) delete up_button;
    if (down_button) delete down_button;
}

AbstractSpinBox::AbstractSpinBox(int x, int y, int width, int height, const String& text)
{
    create(x, y, width, height);
    up_button=NULL;
    down_button=NULL;
    text_input=NULL;
    createUi();
    resizeUi();
    text_input->setText(text);
}

void AbstractSpinBox::createUi()
{
    Grafix* gfx=GetGrafix();
    text_input=new LineInput();
    text_input->setEventHandler(this);
    addChild(text_input);
    up_button=new Button();
    up_button->setEventHandler(this);
    addChild(up_button);
    down_button=new Button();
    down_button->setEventHandler(this);
    addChild(down_button);
    if (gfx) {
        const WidgetStyle& style=GetWidgetStyle();
        img_up=gfx->ButtonSymbolsSmall.getDrawable(3, style.buttonSymbolColor);
        img_down=gfx->ButtonSymbolsSmall.getDrawable(4, style.buttonSymbolColor);
        up_button->setIcon(img_up);
        down_button->setIcon(img_down);
    }
}

void AbstractSpinBox::resizeUi()
{
    Rect client=this->clientRect();
    text_input->setSize(client.width() - 25, client.height());
    text_input->setPos(client.left(), client.top());

    up_button->setSize(25, client.height() / 2);
    up_button->setPos(client.width() - 25, client.top());

    down_button->setSize(25, client.height() / 2);
    down_button->setPos(client.width() - 25, client.top() + client.height() / 2);
}

const Color& AbstractSpinBox::color() const
{
    return text_input->color();
}

void AbstractSpinBox::setColor(const Color& c)
{
    text_input->setColor(c);
}

void AbstractSpinBox::setBackgroundColor(const ppl7::grafix::Color& color)
{
    text_input->setBackgroundColor(color);
}

const Color& AbstractSpinBox::backgroundColor() const
{
    return text_input->backgroundColor();
}

const Font& AbstractSpinBox::font() const
{
    return text_input->font();
}

void AbstractSpinBox::setFont(const Font& font)
{
    text_input->setFont(font);
}

void AbstractSpinBox::setText(const String& value)
{
    text_input->setText(value);
    Event new_event(Event::TextChanged);
    new_event.setWidget(this);
    EventHandler::textChangedEvent(&new_event, value);
}

String AbstractSpinBox::text() const
{
    return String(text_input->text());
}

String AbstractSpinBox::widgetType() const
{
    return "AbstractSpinBox";
}

void AbstractSpinBox::debugEvent(const ppl7::String& name, Event* event)
{
    return;
    Widget* w=event->widget();
    ppl7::String wname="unknown";
    if (w == this) wname="AbstractSpinBox";
    else if (w == up_button) wname="UpButton";
    else if (w == down_button) wname="DownButton";
    else if (w == text_input) wname="TextInput";

    printf("Event [%s]: %s\n", (const char*)wname, (const char*)name);

}

void AbstractSpinBox::setInputValidator(InputValidator* validator)
{
    text_input->setInputValidator(validator);
}

void AbstractSpinBox::paint(Drawable& draw)
{

}

void AbstractSpinBox::mouseDownEvent(MouseEvent* event)
{
    debugEvent("AbstractSpinBox::mouseDownEvent", event);
    Widget* w=event->widget();
    if (w == up_button) stepUp();
    else if (w == down_button) stepDown();
    GetWindowManager()->setKeyboardFocus(text_input);

}

void AbstractSpinBox::gotFocusEvent(FocusEvent* event)
{
    debugEvent("AbstractSpinBox::gotFocusEvent", event);
    GetWindowManager()->setKeyboardFocus(text_input);
}

void AbstractSpinBox::lostFocusEvent(FocusEvent* event)
{
    debugEvent("AbstractSpinBox::lostFocusEvent", event);
}

void AbstractSpinBox::textInputEvent(TextInputEvent* event)
{
    debugEvent("AbstractSpinBox::textInputEvent", event);
}

void AbstractSpinBox::keyDownEvent(KeyEvent* event)
{
    debugEvent("AbstractSpinBox::keyDownEvent", event);
    KeyEvent new_event=*event;
    new_event.setWidget(this);
    EventHandler::keyDownEvent(&new_event);
}

void AbstractSpinBox::keyUpEvent(KeyEvent* event)
{
    debugEvent("AbstractSpinBox::keyUpEvent", event);
}

void AbstractSpinBox::textChangedEvent(Event* event, const String& text)
{
    debugEvent("AbstractSpinBox::textChangedEvent", event);
    Event new_event=*event;
    new_event.setWidget(this);
    EventHandler::textChangedEvent(&new_event, text);
}

SpinBox::SpinBox()
{
    my_value=0;
    step_size=1;
    min=0;
    max=0;
    setInputValidator(this);
}

SpinBox::SpinBox(int x, int y, int width, int height, int64_t value)
    :AbstractSpinBox(x, y, width, height)
{
    my_value=0;
    step_size=1;
    min=0;
    max=0;
    setInputValidator(this);
    if (value < min) min=value;
    if (value > max) max=value;
    setValue(value);
}

String SpinBox::widgetType() const
{
    return "SpinBox";
}

void SpinBox::setValue(int64_t value)
{
    if (value<min || value>max) return;
    my_value=value;
    setText(ppl7::ToString("%ld", value));
}

int64_t SpinBox::value() const
{
    return text().toInt64();
}

void SpinBox::setMinimum(int64_t value)
{
    min=value;
    if (my_value < min) my_value=min;
}

void SpinBox::setMaximum(int64_t value)
{
    max=value;
    if (my_value > max) my_value=max;
}

void SpinBox::setLimits(int64_t min, int64_t max)
{
    setMinimum(min);
    setMaximum(max);
}

int64_t SpinBox::minimum() const
{
    return min;
}

int64_t SpinBox::maximum() const
{
    return max;
}

void SpinBox::setStepSize(int64_t value)
{
    step_size=value;
}

int64_t SpinBox::stepSize() const
{
    return step_size;
}

void SpinBox::stepUp()
{
    int64_t v=value() + step_size;
    if (v > max) v=max;
    setValue(v);
}

void SpinBox::stepDown()
{
    int64_t v=value() - step_size;
    if (v < min) v=min;
    setValue(v);
}

bool SpinBox::validateText(const ppl7::WideString& text)
{
    ppl7::String t=text;
    if (t.isEmpty() || t.pregMatch("/^-?[0-9]*$/")) {
        int64_t v=text.toInt64();
        if (v >= min && v <= max) return true;
        return false;
    }
    return false;
}

DoubleSpinBox::DoubleSpinBox()
{
    my_value=0.0f;
    my_decimals=2;
    step_size=1;
    min=0;
    max=0;
    setInputValidator(this);
}

DoubleSpinBox::DoubleSpinBox(int x, int y, int width, int height, double value, int decimals)
    :AbstractSpinBox(x, y, width, height)
{
    my_decimals=decimals;
    step_size=1;
    min=0;
    max=0;
    setInputValidator(this);
    if (value < min) min=value;
    if (value > max) max=value;
    setValue(value);
}

String DoubleSpinBox::widgetType() const
{
    return "DoubleSpinBox";
}

void DoubleSpinBox::setValue(double value)
{
    if (value<min || value>max) return;
    my_value=value;
    ppl7::String format;
    format.setf("%%0.%df", my_decimals);
    setText(ppl7::ToString((const char*)format, value));

}

double DoubleSpinBox::value() const
{
    return text().toDouble();
}

void DoubleSpinBox::setMinimum(double value)
{
    min=value;
    if (my_value < min) my_value=min;
}

void DoubleSpinBox::setMaximum(double value)
{
    max=value;
    if (my_value > max) my_value=max;
}

void DoubleSpinBox::setLimits(double min, double max)
{
    setMinimum(min);
    setMaximum(max);
}

double DoubleSpinBox::minimum() const
{
    return min;
}

double DoubleSpinBox::maximum() const
{
    return max;
}

void DoubleSpinBox::setStepSize(double value)
{
    step_size=value;
}

double DoubleSpinBox::stepSize() const
{
    return step_size;
}

void DoubleSpinBox::stepUp()
{
    int64_t v=value() + step_size;
    if (v > max) v=max;
    setValue(v);
}

void DoubleSpinBox::stepDown()
{
    int64_t v=value() - step_size;
    if (v < min) v=min;
    setValue(v);
}

bool DoubleSpinBox::validateText(const ppl7::WideString& text)
{
    ppl7::String t=text;
    if (t.isEmpty() || t.pregMatch("/^-?[0-9\\.,]*$/")) {
        double v=text.toDouble();
        if (v >= min && v <= max) return true;
    }
    return false;
}

}   // EOF namespace
<|endoftext|>


<commit_before>#include "niven/routing/nodes/GreedyNode.h"
#include <algorithm>

using namespace std;
using namespace emg;
using namespace ent;

namespace niven
{
	GreedyNode::GreedyNode(string segment) : TrieNode(segment, 0)
	{
		// Retrieve the capture name from the route segment.
		this->name = String::trim(String::trim(String::trim(segment, '{'), '}'), '*');
	}

	void GreedyNode::GetMatches(vector<RouteMatch> &results, const vector<string> &segments, int index, const tree &captures)
	{
		tree all = captures;

		// Add all remaining segments (stuck back together) as a capture.
		all.set(this->name, accumulate(
			segments.begin() + index, segments.end(), segments[index - 1],
			[](auto &a, auto &b) { return a + '/' + b; }
		));

		// A greedy node may still have children, retrieve any matches
		// for them (since they may have a higher score).
		if (this->children.size())
		{
			for (; index < segments.size(); index++)
			{
				for (auto &c : this->children)
				{
					if (c.second->IsMatch(segments[index]))
					{
						c.second->GetMatches(results, segments, index + 1, c.second->GetCaptures(all, segments[index]));
					}
				}
			}
		}

		results.push_back({ this->route, all });
	}

	bool GreedyNode::IsMatch(const std::string &segment)
	{
		return true;
	}
}

<commit_msg>Missing numeric header for accumulate function.<commit_after>#include "niven/routing/nodes/GreedyNode.h"
#include <algorithm>
#include <numeric>

using namespace std;
using namespace emg;
using namespace ent;

namespace niven
{
	GreedyNode::GreedyNode(string segment) : TrieNode(segment, 0)
	{
		// Retrieve the capture name from the route segment.
		this->name = String::trim(String::trim(String::trim(segment, '{'), '}'), '*');
	}

	void GreedyNode::GetMatches(vector<RouteMatch> &results, const vector<string> &segments, int index, const tree &captures)
	{
		tree all = captures;

		// Add all remaining segments (stuck back together) as a capture.
		all.set(this->name, accumulate(
			segments.begin() + index, segments.end(), segments[index - 1],
			[](auto &a, auto &b) { return a + '/' + b; }
		));

		// A greedy node may still have children, retrieve any matches
		// for them (since they may have a higher score).
		if (this->children.size())
		{
			for (; index < segments.size(); index++)
			{
				for (auto &c : this->children)
				{
					if (c.second->IsMatch(segments[index]))
					{
						c.second->GetMatches(results, segments, index + 1, c.second->GetCaptures(all, segments[index]));
					}
				}
			}
		}

		results.push_back({ this->route, all });
	}

	bool GreedyNode::IsMatch(const std::string &segment)
	{
		return true;
	}
}

<|endoftext|>


<commit_before>/*
Copyright (c) 2010-2019, Mathieu Labbe - IntRoLab - Universite de Sherbrooke
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the Universite de Sherbrooke nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include <ros/ros.h>
#include <pluginlib/class_list_macros.h>
#include <nodelet/nodelet.h>

#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
#include <pcl_conversions/pcl_conversions.h>
#include <pcl/io/pcd_io.h>
#include <pcl/filters/voxel_grid.h>

#include <pcl_ros/transforms.h>

#include <tf/transform_listener.h>

#include <nav_msgs/Odometry.h>
#include <sensor_msgs/PointCloud2.h>

#include <message_filters/subscriber.h>
#include <message_filters/sync_policies/exact_time.h>

#include <rtabmap_ros/MsgConversion.h>
#include <rtabmap/core/util3d.h>
#include <rtabmap/core/util3d_filtering.h>

namespace rtabmap_ros
{

/**
 * This nodelet can assemble a number of clouds (max_clouds) coming
 * from the same sensor, taking into account the displacement of the robot based on
 * fixed_frame_id, then publish the resulting cloud.
 * If fixed_frame_id is set to "" (empty), the nodelet will subscribe to
 * an odom topic that should have the exact same stamp than to input cloud.
 * The output cloud has the same stamp and frame than the last assembled cloud.
 */
class PointCloudAssembler : public nodelet::Nodelet
{
public:
	PointCloudAssembler() :
		warningThread_(0),
		callbackCalled_(false),
		exactSync_(0),
		maxClouds_(0),
		assemblingTime_(0),
		skipClouds_(0),
		cloudsSkipped_(0),
		waitForTransformDuration_(0.1),
		rangeMin_(0),
		rangeMax_(0),
		voxelSize_(0),
		fixedFrameId_("odom")
	{}

	virtual ~PointCloudAssembler()
	{
		delete exactSync_;

		if(warningThread_)
		{
			callbackCalled_=true;
			warningThread_->join();
			delete warningThread_;
		}
	}

private:
	virtual void onInit()
	{
		ros::NodeHandle & nh = getNodeHandle();
		ros::NodeHandle & pnh = getPrivateNodeHandle();

		int queueSize = 5;

		pnh.param("queue_size", queueSize, queueSize);
		pnh.param("fixed_frame_id", fixedFrameId_, fixedFrameId_);
		pnh.param("max_clouds", maxClouds_, maxClouds_);
		pnh.param("assembling_time", assemblingTime_, assemblingTime_);
		pnh.param("skip_clouds", skipClouds_, skipClouds_);
		pnh.param("wait_for_transform_duration", waitForTransformDuration_, waitForTransformDuration_);
		pnh.param("range_min", rangeMin_, rangeMin_);
		pnh.param("range_max", rangeMax_, rangeMax_);
		pnh.param("voxel_size", voxelSize_, voxelSize_);
		ROS_ASSERT(maxClouds_>=0 && assemblingTime_ >=0);

		cloudsSkipped_ = skipClouds_;

		std::string subscribedTopicsMsg;
		if(!fixedFrameId_.empty())
		{
			cloudSub_ = nh.subscribe("cloud", 1, &PointCloudAssembler::callbackCloud, this);
			subscribedTopicsMsg = uFormat("\n%s subscribed to %s",
								getName().c_str(),
								cloudSub_.getTopic().c_str());
		}
		else
		{
			syncCloudSub_.subscribe(nh, "cloud", 1);
			syncOdomSub_.subscribe(nh, "odom", 1);
			exactSync_ = new message_filters::Synchronizer<syncPolicy>(syncPolicy(queueSize), syncCloudSub_, syncOdomSub_);
			exactSync_->registerCallback(boost::bind(&rtabmap_ros::PointCloudAssembler::callbackCloudOdom, this, _1, _2));
			subscribedTopicsMsg = uFormat("\n%s subscribed to (exact sync):\n   %s,\n   %s",
								getName().c_str(),
								syncCloudSub_.getTopic().c_str(),
								syncOdomSub_.getTopic().c_str());

			warningThread_ = new boost::thread(boost::bind(&PointCloudAssembler::warningLoop, this, subscribedTopicsMsg));
		}

		cloudPub_ = nh.advertise<sensor_msgs::PointCloud2>("assembled_cloud", 1);

		NODELET_INFO("%s", subscribedTopicsMsg.c_str());
	}

	void callbackCloudOdom(
			const sensor_msgs::PointCloud2ConstPtr & cloudMsg,
			const nav_msgs::OdometryConstPtr & odomMsg)
	{
		callbackCalled_ = true;
		rtabmap::Transform odom = rtabmap_ros::transformFromPoseMsg(odomMsg->pose.pose);
		if(!odom.isNull())
		{
			fixedFrameId_ = odomMsg->header.frame_id;
			callbackCloud(cloudMsg);
		}
		else
		{
			NODELET_WARN("Reseting point cloud assembler as null odometry has been received.");
			clouds_.clear();
		}
	}

	void callbackCloud(const sensor_msgs::PointCloud2ConstPtr & cloudMsg)
	{
		if(cloudPub_.getNumSubscribers())
		{
			if(skipClouds_<=0 || cloudsSkipped_ >= skipClouds_)
			{
				cloudsSkipped_ = 0;

				sensor_msgs::PointCloud2Ptr cpy(new sensor_msgs::PointCloud2);
				*cpy = *cloudMsg;
				clouds_.push_back(cpy);

				if(  (int)clouds_.size() >= maxClouds_ && maxClouds_ != 0
					||
					 (double)(*cpy).header.stamp.toSec() >= (double)clouds_[0]->header.stamp.toSec() + assemblingTime_ && assemblingTime_ != 0.0 )
				{
					pcl::PCLPointCloud2Ptr assembled(new pcl::PCLPointCloud2);
					pcl_conversions::toPCL(*clouds_.back(), *assembled);

					for(size_t i=0; i<clouds_.size()-1; ++i)
					{
						rtabmap::Transform t = rtabmap_ros::getTransform(
								clouds_[i]->header.frame_id, //sourceTargetFrame
								fixedFrameId_, //fixedFrame
								clouds_[i]->header.stamp, //stampSource
								clouds_.back()->header.stamp, //stampTarget
								tfListener_,
								waitForTransformDuration_);

						if(t.isNull())
						{
							ROS_ERROR("Cloud not transform all clouds! Resetting...");
							clouds_.clear();
							return;
						}

						pcl::PCLPointCloud2Ptr assembledTmp(new pcl::PCLPointCloud2);
						if(rangeMin_ > 0.0 || rangeMax_ > 0.0)
						{
							pcl::PCLPointCloud2 output2;
							pcl_conversions::toPCL(*clouds_[i], output2);
							rtabmap::LaserScan scan = rtabmap::util3d::laserScanFromPointCloud(output2);
							if(rangeMin_ > 0.0 || rangeMax_ > 0.0)
							{
								scan = rtabmap::util3d::rangeFiltering(scan, rangeMin_, rangeMax_);
							}
							pcl::concatenatePointCloud(*assembled, *rtabmap::util3d::laserScanToPointCloud2(scan, t), *assembledTmp);
						}
						else
						{
							sensor_msgs::PointCloud2 output;
							pcl_ros::transformPointCloud(t.toEigen4f(), *clouds_[i], output);
							pcl::PCLPointCloud2 output2;
							pcl_conversions::toPCL(output, output2);
							pcl::concatenatePointCloud(*assembled, output2, *assembledTmp);
						}

						assembled = assembledTmp;
					}

					sensor_msgs::PointCloud2 rosCloud;
					if(voxelSize_>0.0)
					{
						pcl::VoxelGrid<pcl::PCLPointCloud2> filter;
						filter.setLeafSize(voxelSize_, voxelSize_, voxelSize_);
						filter.setInputCloud(assembled);
						pcl::PCLPointCloud2Ptr output(new pcl::PCLPointCloud2);
						filter.filter(*output);
						pcl_conversions::moveFromPCL(*output, rosCloud);
					}
					else
					{
						pcl_conversions::moveFromPCL(*assembled, rosCloud);
					}
					rosCloud.header = cloudMsg->header;
					cloudPub_.publish(rosCloud);
					clouds_.clear();
				}
			}
			else
			{
				++cloudsSkipped_;
			}
		}
	}

	void warningLoop(const std::string & subscribedTopicsMsg)
	{
		ros::Duration r(5.0);
		while(!callbackCalled_)
		{
			r.sleep();
			if(!callbackCalled_)
			{
				ROS_WARN("%s: Did not receive data since 5 seconds! Make sure the input topics are "
						"published (\"$ rostopic hz my_topic\") and the timestamps in their "
						"header are set. %s",
						getName().c_str(),
						subscribedTopicsMsg.c_str());
			}
		}
	}

private:
	boost::thread * warningThread_;
	bool callbackCalled_;

	ros::Subscriber cloudSub_;
	ros::Publisher cloudPub_;

	typedef message_filters::sync_policies::ExactTime<sensor_msgs::PointCloud2, nav_msgs::Odometry> syncPolicy;
	message_filters::Synchronizer<syncPolicy>* exactSync_;
	message_filters::Subscriber<sensor_msgs::PointCloud2> syncCloudSub_;
	message_filters::Subscriber<nav_msgs::Odometry> syncOdomSub_;

	int maxClouds_;
	int skipClouds_;
	int cloudsSkipped_;
	double assemblingTime_;
	double waitForTransformDuration_;
	double rangeMin_;
	double rangeMax_;
	double voxelSize_;
	std::string fixedFrameId_;
	tf::TransformListener tfListener_;

	std::vector<sensor_msgs::PointCloud2Ptr> clouds_;
};

PLUGINLIB_EXPORT_CLASS(rtabmap_ros::PointCloudAssembler, nodelet::Nodelet);
}

<commit_msg>:lipstick:<commit_after>/*
Copyright (c) 2010-2019, Mathieu Labbe - IntRoLab - Universite de Sherbrooke
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the Universite de Sherbrooke nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include <ros/ros.h>
#include <pluginlib/class_list_macros.h>
#include <nodelet/nodelet.h>

#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
#include <pcl_conversions/pcl_conversions.h>
#include <pcl/io/pcd_io.h>
#include <pcl/filters/voxel_grid.h>

#include <pcl_ros/transforms.h>

#include <tf/transform_listener.h>

#include <nav_msgs/Odometry.h>
#include <sensor_msgs/PointCloud2.h>

#include <message_filters/subscriber.h>
#include <message_filters/sync_policies/exact_time.h>

#include <rtabmap_ros/MsgConversion.h>
#include <rtabmap/core/util3d.h>
#include <rtabmap/core/util3d_filtering.h>

namespace rtabmap_ros
{

/**
 * This nodelet can assemble a number of clouds (max_clouds) coming
 * from the same sensor, taking into account the displacement of the robot based on
 * fixed_frame_id, then publish the resulting cloud.
 * If fixed_frame_id is set to "" (empty), the nodelet will subscribe to
 * an odom topic that should have the exact same stamp than to input cloud.
 * The output cloud has the same stamp and frame than the last assembled cloud.
 */
class PointCloudAssembler : public nodelet::Nodelet
{
public:
	PointCloudAssembler() :
		warningThread_(0),
		callbackCalled_(false),
		exactSync_(0),
		maxClouds_(0),
		assemblingTime_(0),
		skipClouds_(0),
		cloudsSkipped_(0),
		waitForTransformDuration_(0.1),
		rangeMin_(0),
		rangeMax_(0),
		voxelSize_(0),
		fixedFrameId_("odom")
	{}

	virtual ~PointCloudAssembler()
	{
		delete exactSync_;

		if(warningThread_)
		{
			callbackCalled_=true;
			warningThread_->join();
			delete warningThread_;
		}
	}

private:
	virtual void onInit()
	{
		ros::NodeHandle & nh = getNodeHandle();
		ros::NodeHandle & pnh = getPrivateNodeHandle();

		int queueSize = 5;

		pnh.param("queue_size", queueSize, queueSize);
		pnh.param("fixed_frame_id", fixedFrameId_, fixedFrameId_);
		pnh.param("max_clouds", maxClouds_, maxClouds_);
		pnh.param("assembling_time", assemblingTime_, assemblingTime_);
		pnh.param("skip_clouds", skipClouds_, skipClouds_);
		pnh.param("wait_for_transform_duration", waitForTransformDuration_, waitForTransformDuration_);
		pnh.param("range_min", rangeMin_, rangeMin_);
		pnh.param("range_max", rangeMax_, rangeMax_);
		pnh.param("voxel_size", voxelSize_, voxelSize_);
		ROS_ASSERT(maxClouds_>0 || assemblingTime_ >0.0);

		cloudsSkipped_ = skipClouds_;

		std::string subscribedTopicsMsg;
		if(!fixedFrameId_.empty())
		{
			cloudSub_ = nh.subscribe("cloud", 1, &PointCloudAssembler::callbackCloud, this);
			subscribedTopicsMsg = uFormat("\n%s subscribed to %s",
								getName().c_str(),
								cloudSub_.getTopic().c_str());
		}
		else
		{
			syncCloudSub_.subscribe(nh, "cloud", 1);
			syncOdomSub_.subscribe(nh, "odom", 1);
			exactSync_ = new message_filters::Synchronizer<syncPolicy>(syncPolicy(queueSize), syncCloudSub_, syncOdomSub_);
			exactSync_->registerCallback(boost::bind(&rtabmap_ros::PointCloudAssembler::callbackCloudOdom, this, _1, _2));
			subscribedTopicsMsg = uFormat("\n%s subscribed to (exact sync):\n   %s,\n   %s",
								getName().c_str(),
								syncCloudSub_.getTopic().c_str(),
								syncOdomSub_.getTopic().c_str());

			warningThread_ = new boost::thread(boost::bind(&PointCloudAssembler::warningLoop, this, subscribedTopicsMsg));
		}

		cloudPub_ = nh.advertise<sensor_msgs::PointCloud2>("assembled_cloud", 1);

		NODELET_INFO("%s", subscribedTopicsMsg.c_str());
	}

	void callbackCloudOdom(
			const sensor_msgs::PointCloud2ConstPtr & cloudMsg,
			const nav_msgs::OdometryConstPtr & odomMsg)
	{
		callbackCalled_ = true;
		rtabmap::Transform odom = rtabmap_ros::transformFromPoseMsg(odomMsg->pose.pose);
		if(!odom.isNull())
		{
			fixedFrameId_ = odomMsg->header.frame_id;
			callbackCloud(cloudMsg);
		}
		else
		{
			NODELET_WARN("Reseting point cloud assembler as null odometry has been received.");
			clouds_.clear();
		}
	}

	void callbackCloud(const sensor_msgs::PointCloud2ConstPtr & cloudMsg)
	{
		if(cloudPub_.getNumSubscribers())
		{
			if(skipClouds_<=0 || cloudsSkipped_ >= skipClouds_)
			{
				cloudsSkipped_ = 0;

				sensor_msgs::PointCloud2Ptr cpy(new sensor_msgs::PointCloud2);
				*cpy = *cloudMsg;
				clouds_.push_back(cpy);

				if(  (int)clouds_.size() >= maxClouds_ && maxClouds_ > 0
					||
					 (double)(*cpy).header.stamp.toSec() >= (double)clouds_[0]->header.stamp.toSec() + assemblingTime_ && assemblingTime_ > 0.0 )
				{
					pcl::PCLPointCloud2Ptr assembled(new pcl::PCLPointCloud2);
					pcl_conversions::toPCL(*clouds_.back(), *assembled);

					for(size_t i=0; i<clouds_.size()-1; ++i)
					{
						rtabmap::Transform t = rtabmap_ros::getTransform(
								clouds_[i]->header.frame_id, //sourceTargetFrame
								fixedFrameId_, //fixedFrame
								clouds_[i]->header.stamp, //stampSource
								clouds_.back()->header.stamp, //stampTarget
								tfListener_,
								waitForTransformDuration_);

						if(t.isNull())
						{
							ROS_ERROR("Cloud not transform all clouds! Resetting...");
							clouds_.clear();
							return;
						}

						pcl::PCLPointCloud2Ptr assembledTmp(new pcl::PCLPointCloud2);
						if(rangeMin_ > 0.0 || rangeMax_ > 0.0)
						{
							pcl::PCLPointCloud2 output2;
							pcl_conversions::toPCL(*clouds_[i], output2);
							rtabmap::LaserScan scan = rtabmap::util3d::laserScanFromPointCloud(output2);
							if(rangeMin_ > 0.0 || rangeMax_ > 0.0)
							{
								scan = rtabmap::util3d::rangeFiltering(scan, rangeMin_, rangeMax_);
							}
							pcl::concatenatePointCloud(*assembled, *rtabmap::util3d::laserScanToPointCloud2(scan, t), *assembledTmp);
						}
						else
						{
							sensor_msgs::PointCloud2 output;
							pcl_ros::transformPointCloud(t.toEigen4f(), *clouds_[i], output);
							pcl::PCLPointCloud2 output2;
							pcl_conversions::toPCL(output, output2);
							pcl::concatenatePointCloud(*assembled, output2, *assembledTmp);
						}

						assembled = assembledTmp;
					}

					sensor_msgs::PointCloud2 rosCloud;
					if(voxelSize_>0.0)
					{
						pcl::VoxelGrid<pcl::PCLPointCloud2> filter;
						filter.setLeafSize(voxelSize_, voxelSize_, voxelSize_);
						filter.setInputCloud(assembled);
						pcl::PCLPointCloud2Ptr output(new pcl::PCLPointCloud2);
						filter.filter(*output);
						pcl_conversions::moveFromPCL(*output, rosCloud);
					}
					else
					{
						pcl_conversions::moveFromPCL(*assembled, rosCloud);
					}
					rosCloud.header = cloudMsg->header;
					cloudPub_.publish(rosCloud);
					clouds_.clear();
				}
			}
			else
			{
				++cloudsSkipped_;
			}
		}
	}

	void warningLoop(const std::string & subscribedTopicsMsg)
	{
		ros::Duration r(5.0);
		while(!callbackCalled_)
		{
			r.sleep();
			if(!callbackCalled_)
			{
				ROS_WARN("%s: Did not receive data since 5 seconds! Make sure the input topics are "
						"published (\"$ rostopic hz my_topic\") and the timestamps in their "
						"header are set. %s",
						getName().c_str(),
						subscribedTopicsMsg.c_str());
			}
		}
	}

private:
	boost::thread * warningThread_;
	bool callbackCalled_;

	ros::Subscriber cloudSub_;
	ros::Publisher cloudPub_;

	typedef message_filters::sync_policies::ExactTime<sensor_msgs::PointCloud2, nav_msgs::Odometry> syncPolicy;
	message_filters::Synchronizer<syncPolicy>* exactSync_;
	message_filters::Subscriber<sensor_msgs::PointCloud2> syncCloudSub_;
	message_filters::Subscriber<nav_msgs::Odometry> syncOdomSub_;

	int maxClouds_;
	int skipClouds_;
	int cloudsSkipped_;
	double assemblingTime_;
	double waitForTransformDuration_;
	double rangeMin_;
	double rangeMax_;
	double voxelSize_;
	std::string fixedFrameId_;
	tf::TransformListener tfListener_;

	std::vector<sensor_msgs::PointCloud2Ptr> clouds_;
};

PLUGINLIB_EXPORT_CLASS(rtabmap_ros::PointCloudAssembler, nodelet::Nodelet);
}

<|endoftext|>


<commit_before>/*
 * Copyright (c) 2014 New Designs Unlimited, LLC
 * Opensource Automated License Plate Recognition [http://www.openalpr.com]
 *
 * This file is part of OpenAlpr.
 *
 * OpenAlpr is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License
 * version 3 as published by the Free Software Foundation
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include <opencv2/core/core.hpp>

#include "licenseplatecandidate.h"

using namespace std;
using namespace cv;

LicensePlateCandidate::LicensePlateCandidate(PipelineData* pipeline_data)
{
  this->pipeline_data = pipeline_data;
  this->config = pipeline_data->config;

}

LicensePlateCandidate::~LicensePlateCandidate()
{
  delete charSegmenter;
}

// Must delete this pointer in parent class
void LicensePlateCandidate::recognize()
{
  charSegmenter = NULL;

  pipeline_data->plate_area_confidence = 0;
  pipeline_data->isMultiline = config->multiline;

  int expandX = round(this->pipeline_data->regionOfInterest.width * 0.20);
  int expandY = round(this->pipeline_data->regionOfInterest.height * 0.15);
  // expand box by 15% in all directions
  Rect expandedRegion = expandRect( this->pipeline_data->regionOfInterest, expandX, expandY, this->pipeline_data->grayImg.cols, this->pipeline_data->grayImg.rows) ;

  pipeline_data->crop_gray = Mat(this->pipeline_data->grayImg, expandedRegion);
  resize(pipeline_data->crop_gray, pipeline_data->crop_gray, Size(config->templateWidthPx, config->templateHeightPx));

  CharacterRegion charRegion(pipeline_data);

  if (charRegion.confidence > 10)
  {
    PlateLines plateLines(pipeline_data);

    if (pipeline_data->hasPlateBorder)
      plateLines.processImage(pipeline_data->plateBorderMask, 1.10);

    plateLines.processImage(pipeline_data->crop_gray, 0.9);

    PlateCorners cornerFinder(pipeline_data->crop_gray, &plateLines, pipeline_data);
    vector<Point> smallPlateCorners = cornerFinder.findPlateCorners();

    if (cornerFinder.confidence > 0)
    {

      timespec startTime;
      getTime(&startTime);

      Mat originalCrop = pipeline_data->crop_gray;

      pipeline_data->plate_corners = transformPointsToOriginalImage(this->pipeline_data->grayImg, pipeline_data->crop_gray, expandedRegion, smallPlateCorners);

      Size outputImageSize = getOutputImageSize(pipeline_data->plate_corners);
      Mat transmtx = getTransformationMatrix(pipeline_data->plate_corners, outputImageSize);
      pipeline_data->crop_gray = deSkewPlate(this->pipeline_data->grayImg, outputImageSize, transmtx);

      // Apply a perspective transformation to the TextLine objects
      // to match the newly deskewed license plate crop
      vector<TextLine> newLines;
      for (uint i = 0; i < pipeline_data->textLines.size(); i++)
      {
        vector<Point2f> textArea = transformPointsToOriginalImage(this->pipeline_data->grayImg, originalCrop, expandedRegion,
                pipeline_data->textLines[i].textArea);
        vector<Point2f> linePolygon = transformPointsToOriginalImage(this->pipeline_data->grayImg, originalCrop, expandedRegion,
                pipeline_data->textLines[i].linePolygon);

        vector<Point2f> textAreaRemapped;
        vector<Point2f> linePolygonRemapped;

        perspectiveTransform(textArea, textAreaRemapped, transmtx);
        perspectiveTransform(linePolygon, linePolygonRemapped, transmtx);

        newLines.push_back(TextLine(textAreaRemapped, linePolygonRemapped));
      }

      pipeline_data->textLines.clear();
      for (uint i = 0; i < newLines.size(); i++)
        pipeline_data->textLines.push_back(newLines[i]);

      if (config->debugTiming)
      {
        timespec endTime;
        getTime(&endTime);
        cout << "deskew Time: " << diffclock(startTime, endTime) << "ms." << endl;
      }

      charSegmenter = new CharacterSegmenter(pipeline_data);

      pipeline_data->plate_area_confidence = 100;
    }
    charRegion.confidence = 0;
  }
}

// Re-maps the coordinates from the smallImage to the coordinate space of the bigImage.
vector<Point2f> LicensePlateCandidate::transformPointsToOriginalImage(Mat bigImage, Mat smallImage, Rect region, vector<Point> corners)
{
  vector<Point2f> cornerPoints;
  for (uint i = 0; i < corners.size(); i++)
  {
    float bigX = (corners[i].x * ((float) region.width / smallImage.cols));
    float bigY = (corners[i].y * ((float) region.height / smallImage.rows));

    bigX = bigX + region.x;
    bigY = bigY + region.y;

    cornerPoints.push_back(Point2f(bigX, bigY));
  }

  return cornerPoints;
}

Size LicensePlateCandidate::getOutputImageSize(vector<Point2f> corners)
{
  // Figure out the approximate width/height of the license plate region, so we can maintain the aspect ratio.
  LineSegment leftEdge(round(corners[3].x), round(corners[3].y), round(corners[0].x), round(corners[0].y));
  LineSegment rightEdge(round(corners[2].x), round(corners[2].y), round(corners[1].x), round(corners[1].y));
  LineSegment topEdge(round(corners[0].x), round(corners[0].y), round(corners[1].x), round(corners[1].y));
  LineSegment bottomEdge(round(corners[3].x), round(corners[3].y), round(corners[2].x), round(corners[2].y));

  float w = distanceBetweenPoints(leftEdge.midpoint(), rightEdge.midpoint());
  float h = distanceBetweenPoints(bottomEdge.midpoint(), topEdge.midpoint());
  float aspect = w/h;
  int width = config->ocrImageWidthPx;
  int height = round(((float) width) / aspect);
  if (height > config->ocrImageHeightPx)
  {
    height = config->ocrImageHeightPx;
    width = round(((float) height) * aspect);
  }

  return Size(width, height);
}

Mat LicensePlateCandidate::getTransformationMatrix(vector<Point2f> corners, Size outputImageSize)
{
  // Corners of the destination image
  vector<Point2f> quad_pts;
  quad_pts.push_back(Point2f(0, 0));
  quad_pts.push_back(Point2f(outputImageSize.width, 0));
  quad_pts.push_back(Point2f(outputImageSize.width, outputImageSize.height));
  quad_pts.push_back(Point2f(0, outputImageSize.height));

  // Get transformation matrix
  Mat transmtx = getPerspectiveTransform(corners, quad_pts);

  return transmtx;
}

Mat LicensePlateCandidate::deSkewPlate(Mat inputImage, Size outputImageSize, Mat transformationMatrix)
{

  Mat deskewed(outputImageSize, this->pipeline_data->grayImg.type());

  // Apply perspective transformation to the image
  warpPerspective(inputImage, deskewed, transformationMatrix, deskewed.size(), INTER_CUBIC);

  if (this->config->debugGeneral)
    displayImage(config, "quadrilateral", deskewed);

  return deskewed;
}

<commit_msg>Got rid of expanding the region before doing text analysis<commit_after>/*
 * Copyright (c) 2014 New Designs Unlimited, LLC
 * Opensource Automated License Plate Recognition [http://www.openalpr.com]
 *
 * This file is part of OpenAlpr.
 *
 * OpenAlpr is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License
 * version 3 as published by the Free Software Foundation
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include <opencv2/core/core.hpp>

#include "licenseplatecandidate.h"

using namespace std;
using namespace cv;

LicensePlateCandidate::LicensePlateCandidate(PipelineData* pipeline_data)
{
  this->pipeline_data = pipeline_data;
  this->config = pipeline_data->config;

}

LicensePlateCandidate::~LicensePlateCandidate()
{
  delete charSegmenter;
}

// Must delete this pointer in parent class
void LicensePlateCandidate::recognize()
{
  charSegmenter = NULL;

  pipeline_data->plate_area_confidence = 0;
  pipeline_data->isMultiline = config->multiline;

  Rect expandedRegion = this->pipeline_data->regionOfInterest;

  pipeline_data->crop_gray = Mat(this->pipeline_data->grayImg, expandedRegion);
  resize(pipeline_data->crop_gray, pipeline_data->crop_gray, Size(config->templateWidthPx, config->templateHeightPx));

  CharacterRegion charRegion(pipeline_data);

  if (charRegion.confidence > 10)
  {
    PlateLines plateLines(pipeline_data);

    if (pipeline_data->hasPlateBorder)
      plateLines.processImage(pipeline_data->plateBorderMask, 1.10);

    plateLines.processImage(pipeline_data->crop_gray, 0.9);

    PlateCorners cornerFinder(pipeline_data->crop_gray, &plateLines, pipeline_data);
    vector<Point> smallPlateCorners = cornerFinder.findPlateCorners();

    if (cornerFinder.confidence > 0)
    {

      timespec startTime;
      getTime(&startTime);

      Mat originalCrop = pipeline_data->crop_gray;

      pipeline_data->plate_corners = transformPointsToOriginalImage(this->pipeline_data->grayImg, pipeline_data->crop_gray, expandedRegion, smallPlateCorners);

      Size outputImageSize = getOutputImageSize(pipeline_data->plate_corners);
      Mat transmtx = getTransformationMatrix(pipeline_data->plate_corners, outputImageSize);
      pipeline_data->crop_gray = deSkewPlate(this->pipeline_data->grayImg, outputImageSize, transmtx);

      // Apply a perspective transformation to the TextLine objects
      // to match the newly deskewed license plate crop
      vector<TextLine> newLines;
      for (uint i = 0; i < pipeline_data->textLines.size(); i++)
      {
        vector<Point2f> textArea = transformPointsToOriginalImage(this->pipeline_data->grayImg, originalCrop, expandedRegion,
                pipeline_data->textLines[i].textArea);
        vector<Point2f> linePolygon = transformPointsToOriginalImage(this->pipeline_data->grayImg, originalCrop, expandedRegion,
                pipeline_data->textLines[i].linePolygon);

        vector<Point2f> textAreaRemapped;
        vector<Point2f> linePolygonRemapped;

        perspectiveTransform(textArea, textAreaRemapped, transmtx);
        perspectiveTransform(linePolygon, linePolygonRemapped, transmtx);

        newLines.push_back(TextLine(textAreaRemapped, linePolygonRemapped));
      }

      pipeline_data->textLines.clear();
      for (uint i = 0; i < newLines.size(); i++)
        pipeline_data->textLines.push_back(newLines[i]);

      if (config->debugTiming)
      {
        timespec endTime;
        getTime(&endTime);
        cout << "deskew Time: " << diffclock(startTime, endTime) << "ms." << endl;
      }

      charSegmenter = new CharacterSegmenter(pipeline_data);

      pipeline_data->plate_area_confidence = 100;
    }
    charRegion.confidence = 0;
  }
}

// Re-maps the coordinates from the smallImage to the coordinate space of the bigImage.
vector<Point2f> LicensePlateCandidate::transformPointsToOriginalImage(Mat bigImage, Mat smallImage, Rect region, vector<Point> corners)
{
  vector<Point2f> cornerPoints;
  for (uint i = 0; i < corners.size(); i++)
  {
    float bigX = (corners[i].x * ((float) region.width / smallImage.cols));
    float bigY = (corners[i].y * ((float) region.height / smallImage.rows));

    bigX = bigX + region.x;
    bigY = bigY + region.y;

    cornerPoints.push_back(Point2f(bigX, bigY));
  }

  return cornerPoints;
}

Size LicensePlateCandidate::getOutputImageSize(vector<Point2f> corners)
{
  // Figure out the approximate width/height of the license plate region, so we can maintain the aspect ratio.
  LineSegment leftEdge(round(corners[3].x), round(corners[3].y), round(corners[0].x), round(corners[0].y));
  LineSegment rightEdge(round(corners[2].x), round(corners[2].y), round(corners[1].x), round(corners[1].y));
  LineSegment topEdge(round(corners[0].x), round(corners[0].y), round(corners[1].x), round(corners[1].y));
  LineSegment bottomEdge(round(corners[3].x), round(corners[3].y), round(corners[2].x), round(corners[2].y));

  float w = distanceBetweenPoints(leftEdge.midpoint(), rightEdge.midpoint());
  float h = distanceBetweenPoints(bottomEdge.midpoint(), topEdge.midpoint());
  float aspect = w/h;
  int width = config->ocrImageWidthPx;
  int height = round(((float) width) / aspect);
  if (height > config->ocrImageHeightPx)
  {
    height = config->ocrImageHeightPx;
    width = round(((float) height) * aspect);
  }

  return Size(width, height);
}

Mat LicensePlateCandidate::getTransformationMatrix(vector<Point2f> corners, Size outputImageSize)
{
  // Corners of the destination image
  vector<Point2f> quad_pts;
  quad_pts.push_back(Point2f(0, 0));
  quad_pts.push_back(Point2f(outputImageSize.width, 0));
  quad_pts.push_back(Point2f(outputImageSize.width, outputImageSize.height));
  quad_pts.push_back(Point2f(0, outputImageSize.height));

  // Get transformation matrix
  Mat transmtx = getPerspectiveTransform(corners, quad_pts);

  return transmtx;
}

Mat LicensePlateCandidate::deSkewPlate(Mat inputImage, Size outputImageSize, Mat transformationMatrix)
{

  Mat deskewed(outputImageSize, this->pipeline_data->grayImg.type());

  // Apply perspective transformation to the image
  warpPerspective(inputImage, deskewed, transformationMatrix, deskewed.size(), INTER_CUBIC);

  if (this->config->debugGeneral)
    displayImage(config, "quadrilateral", deskewed);

  return deskewed;
}

<|endoftext|>


<commit_before>#include "openmc/wmp.h"

#include "openmc/constants.h"
#include "openmc/cross_sections.h"
#include "openmc/hdf5_interface.h"
#include "openmc/math_functions.h"
#include "openmc/nuclide.h"

#include <fmt/core.h>

#include <cmath>

namespace openmc {

//========================================================================
// WindowedeMultipole implementation
//========================================================================

WindowedMultipole::WindowedMultipole(hid_t group)
{
  // Get name of nuclide from group, removing leading '/'
  name_ = object_name(group).substr(1);

  // Read scalar values.
  read_dataset(group, "spacing", spacing_);
  read_dataset(group, "sqrtAWR", sqrt_awr_);
  read_dataset(group, "E_min", E_min_);
  read_dataset(group, "E_max", E_max_);

  // Read the "data" array.  Use its shape to figure out the number of poles
  // and residue types in this data.
  read_dataset(group, "data", data_);
  int n_residues = data_.shape()[1] - 1;

  // Check to see if this data includes fission residues.
  fissionable_ = (n_residues == 3);

  // Read the "windows" array and use its shape to figure out the number of
  // windows.
  read_dataset(group, "windows", windows_);
  int n_windows = windows_.shape()[0];

  // Read the "broaden_poly" arrays.
  read_dataset(group, "broaden_poly", broaden_poly_);
  if (n_windows != broaden_poly_.shape()[0]) {
    fatal_error("broaden_poly array shape is not consistent with the windows "
      "array shape in WMP library for " + name_ + ".");
  }

  // Read the "curvefit" array.
  read_dataset(group, "curvefit", curvefit_);
  if (n_windows != broaden_poly_.shape()[0]) {
    fatal_error("curvefit array shape is not consistent with the windows "
      "array shape in WMP library for " + name_ + ".");
  }
  fit_order_ = curvefit_.shape()[1] - 1;
}

std::tuple<double, double, double>
WindowedMultipole::evaluate(double E, double sqrtkT)
{
  using namespace std::complex_literals;

  // ==========================================================================
  // Bookkeeping

  // Define some frequently used variables.
  double sqrtE = std::sqrt(E);
  double invE = 1.0 / E;

  // Locate window containing energy
  int i_window = (sqrtE - std::sqrt(E_min_)) / spacing_;
  int startw = windows_(i_window, 0) - 1;
  int endw = windows_(i_window, 1) - 1;

  // Initialize the ouptut cross sections
  double sig_s = 0.0;
  double sig_a = 0.0;
  double sig_f = 0.0;

  // ==========================================================================
  // Add the contribution from the curvefit polynomial.

  if (sqrtkT > 0.0 && broaden_poly_(i_window)) {
    // Broaden the curvefit.
    double dopp = sqrt_awr_ / sqrtkT;
    std::vector<double> broadened_polynomials(fit_order_ + 1);
    broaden_wmp_polynomials(E, dopp, fit_order_ + 1, broadened_polynomials.data());
    for (int i_poly = 0; i_poly < fit_order_ + 1; ++i_poly) {
      sig_s += curvefit_(i_window, i_poly, FIT_S) * broadened_polynomials[i_poly];
      sig_a += curvefit_(i_window, i_poly, FIT_A) * broadened_polynomials[i_poly];
      if (fissionable_) {
        sig_f += curvefit_(i_window, i_poly, FIT_F) * broadened_polynomials[i_poly];
      }
    }
  } else {
    // Evaluate as if it were a polynomial
    double temp = invE;
    for (int i_poly = 0; i_poly < fit_order_ + 1; ++i_poly) {
      sig_s += curvefit_(i_window, i_poly, FIT_S) * temp;
      sig_a += curvefit_(i_window, i_poly, FIT_A) * temp;
      if (fissionable_) {
        sig_f += curvefit_(i_window, i_poly, FIT_F) * temp;
      }
      temp *= sqrtE;
    }
  }

  // ==========================================================================
  // Add the contribution from the poles in this window.

  if (sqrtkT == 0.0) {
    // If at 0K, use asymptotic form.
    for (int i_pole = startw; i_pole <= endw; ++i_pole) {
      std::complex<double> psi_chi = -1.0i / (data_(i_pole, MP_EA) - sqrtE);
      std::complex<double> c_temp = psi_chi / E;
      sig_s += (data_(i_pole, MP_RS) * c_temp).real();
      sig_a += (data_(i_pole, MP_RA) * c_temp).real();
      if (fissionable_) {
        sig_f += (data_(i_pole, MP_RF) * c_temp).real();
      }
    }
  } else {
    // At temperature, use Faddeeva function-based form.
    double dopp = sqrt_awr_ / sqrtkT;
    if (endw >= startw) {
      for (int i_pole = startw; i_pole <= endw; ++i_pole) {
        std::complex<double> z = (sqrtE - data_(i_pole, MP_EA)) * dopp;
        std::complex<double> w_val = faddeeva(z) * dopp * invE * SQRT_PI;
        sig_s += (data_(i_pole, MP_RS) * w_val).real();
        sig_a += (data_(i_pole, MP_RA) * w_val).real();
        if (fissionable_) {
          sig_f += (data_(i_pole, MP_RF) * w_val).real();
        }
      }
    }
  }

  return std::make_tuple(sig_s, sig_a, sig_f);
}

std::tuple<double, double, double>
WindowedMultipole::evaluate_deriv(double E, double sqrtkT)
{
  // ==========================================================================
  // Bookkeeping

  // Define some frequently used variables.
  double sqrtE = std::sqrt(E);
  double invE = 1.0 / E;
  double T = sqrtkT*sqrtkT / K_BOLTZMANN;

  if (sqrtkT == 0.0) {
    fatal_error("Windowed multipole temperature derivatives are not implemented"
      " for 0 Kelvin cross sections.");
  }

  // Locate us
  int i_window = (sqrtE - std::sqrt(E_min_)) / spacing_;
  int startw = windows_(i_window, 0) - 1;
  int endw = windows_(i_window, 1) - 1;

  // Initialize the ouptut cross sections.
  double sig_s = 0.0;
  double sig_a = 0.0;
  double sig_f = 0.0;

  // TODO Polynomials: Some of the curvefit polynomials Doppler broaden so
  // rigorously we should be computing the derivative of those.  But in
  // practice, those derivatives are only large at very low energy and they
  // have no effect on reactor calculations.

  // ==========================================================================
  // Add the contribution from the poles in this window.

  double dopp = sqrt_awr_ / sqrtkT;
  if (endw >= startw) {
    for (int i_pole = startw; i_pole <= endw; ++i_pole) {
      std::complex<double> z = (sqrtE - data_(i_pole, MP_EA)) * dopp;
      std::complex<double> w_val = -invE * SQRT_PI * 0.5 * w_derivative(z, 2);
      sig_s += (data_(i_pole, MP_RS) * w_val).real();
      sig_a += (data_(i_pole, MP_RA) * w_val).real();
      if (fissionable_) {
        sig_f += (data_(i_pole, MP_RF) * w_val).real();
      }
    }
    sig_s *= -0.5*sqrt_awr_ / std::sqrt(K_BOLTZMANN) * std::pow(T, -1.5);
    sig_a *= -0.5*sqrt_awr_ / std::sqrt(K_BOLTZMANN) * std::pow(T, -1.5);
    sig_f *= -0.5*sqrt_awr_ / std::sqrt(K_BOLTZMANN) * std::pow(T, -1.5);
  }

  return std::make_tuple(sig_s, sig_a, sig_f);
}

//========================================================================
// Non-member functions
//========================================================================

void check_wmp_version(hid_t file)
{
  if (attribute_exists(file, "version")) {
    std::array<int, 2> version;
    read_attribute(file, "version", version);
    if (version[0] != WMP_VERSION[0]) {
      fatal_error(fmt::format(
        "WMP data format uses version {}.{} whereas your installation of "
        "OpenMC expects version {}.x data.",
        version[0], version[1], WMP_VERSION[0]));
    }
  } else {
    fatal_error(fmt::format("WMP data does not indicate a version. Your "
      "installation of OpenMC expects version {}x data.", WMP_VERSION[0]));
  }
}

void read_multipole_data(int i_nuclide)
{
  // Look for WMP data in cross_sections.xml
  const auto& nuc {data::nuclides[i_nuclide]};
  auto it = data::library_map.find({Library::Type::wmp, nuc->name_});

  // If no WMP library for this nuclide, just return
  if (it == data::library_map.end()) return;

  // Check if WMP library exists
  int idx = it->second;
  std::string& filename = data::libraries[idx].path_;

  // Display message
  write_message("Reading " + nuc->name_ + " WMP data from " + filename, 6);

  // Open file and make sure version is sufficient
  hid_t file = file_open(filename, 'r');
  check_wmp_version(file);

  // Read nuclide data from HDF5
  hid_t group = open_group(file, nuc->name_.c_str());
  nuc->multipole_ = std::make_unique<WindowedMultipole>(group);
  close_group(group);
  file_close(file);
}

} // namespace openmc
<commit_msg>Apply templated write_message to wmp.cpp<commit_after>#include "openmc/wmp.h"

#include "openmc/constants.h"
#include "openmc/cross_sections.h"
#include "openmc/hdf5_interface.h"
#include "openmc/math_functions.h"
#include "openmc/nuclide.h"
#include "openmc/error.h"  // for writing messages

#include <fmt/core.h>

#include <cmath>

namespace openmc {

//========================================================================
// WindowedeMultipole implementation
//========================================================================

WindowedMultipole::WindowedMultipole(hid_t group)
{
  // Get name of nuclide from group, removing leading '/'
  name_ = object_name(group).substr(1);

  // Read scalar values.
  read_dataset(group, "spacing", spacing_);
  read_dataset(group, "sqrtAWR", sqrt_awr_);
  read_dataset(group, "E_min", E_min_);
  read_dataset(group, "E_max", E_max_);

  // Read the "data" array.  Use its shape to figure out the number of poles
  // and residue types in this data.
  read_dataset(group, "data", data_);
  int n_residues = data_.shape()[1] - 1;

  // Check to see if this data includes fission residues.
  fissionable_ = (n_residues == 3);

  // Read the "windows" array and use its shape to figure out the number of
  // windows.
  read_dataset(group, "windows", windows_);
  int n_windows = windows_.shape()[0];

  // Read the "broaden_poly" arrays.
  read_dataset(group, "broaden_poly", broaden_poly_);
  if (n_windows != broaden_poly_.shape()[0]) {
    fatal_error("broaden_poly array shape is not consistent with the windows "
      "array shape in WMP library for " + name_ + ".");
  }

  // Read the "curvefit" array.
  read_dataset(group, "curvefit", curvefit_);
  if (n_windows != broaden_poly_.shape()[0]) {
    fatal_error("curvefit array shape is not consistent with the windows "
      "array shape in WMP library for " + name_ + ".");
  }
  fit_order_ = curvefit_.shape()[1] - 1;
}

std::tuple<double, double, double>
WindowedMultipole::evaluate(double E, double sqrtkT)
{
  using namespace std::complex_literals;

  // ==========================================================================
  // Bookkeeping

  // Define some frequently used variables.
  double sqrtE = std::sqrt(E);
  double invE = 1.0 / E;

  // Locate window containing energy
  int i_window = (sqrtE - std::sqrt(E_min_)) / spacing_;
  int startw = windows_(i_window, 0) - 1;
  int endw = windows_(i_window, 1) - 1;

  // Initialize the ouptut cross sections
  double sig_s = 0.0;
  double sig_a = 0.0;
  double sig_f = 0.0;

  // ==========================================================================
  // Add the contribution from the curvefit polynomial.

  if (sqrtkT > 0.0 && broaden_poly_(i_window)) {
    // Broaden the curvefit.
    double dopp = sqrt_awr_ / sqrtkT;
    std::vector<double> broadened_polynomials(fit_order_ + 1);
    broaden_wmp_polynomials(E, dopp, fit_order_ + 1, broadened_polynomials.data());
    for (int i_poly = 0; i_poly < fit_order_ + 1; ++i_poly) {
      sig_s += curvefit_(i_window, i_poly, FIT_S) * broadened_polynomials[i_poly];
      sig_a += curvefit_(i_window, i_poly, FIT_A) * broadened_polynomials[i_poly];
      if (fissionable_) {
        sig_f += curvefit_(i_window, i_poly, FIT_F) * broadened_polynomials[i_poly];
      }
    }
  } else {
    // Evaluate as if it were a polynomial
    double temp = invE;
    for (int i_poly = 0; i_poly < fit_order_ + 1; ++i_poly) {
      sig_s += curvefit_(i_window, i_poly, FIT_S) * temp;
      sig_a += curvefit_(i_window, i_poly, FIT_A) * temp;
      if (fissionable_) {
        sig_f += curvefit_(i_window, i_poly, FIT_F) * temp;
      }
      temp *= sqrtE;
    }
  }

  // ==========================================================================
  // Add the contribution from the poles in this window.

  if (sqrtkT == 0.0) {
    // If at 0K, use asymptotic form.
    for (int i_pole = startw; i_pole <= endw; ++i_pole) {
      std::complex<double> psi_chi = -1.0i / (data_(i_pole, MP_EA) - sqrtE);
      std::complex<double> c_temp = psi_chi / E;
      sig_s += (data_(i_pole, MP_RS) * c_temp).real();
      sig_a += (data_(i_pole, MP_RA) * c_temp).real();
      if (fissionable_) {
        sig_f += (data_(i_pole, MP_RF) * c_temp).real();
      }
    }
  } else {
    // At temperature, use Faddeeva function-based form.
    double dopp = sqrt_awr_ / sqrtkT;
    if (endw >= startw) {
      for (int i_pole = startw; i_pole <= endw; ++i_pole) {
        std::complex<double> z = (sqrtE - data_(i_pole, MP_EA)) * dopp;
        std::complex<double> w_val = faddeeva(z) * dopp * invE * SQRT_PI;
        sig_s += (data_(i_pole, MP_RS) * w_val).real();
        sig_a += (data_(i_pole, MP_RA) * w_val).real();
        if (fissionable_) {
          sig_f += (data_(i_pole, MP_RF) * w_val).real();
        }
      }
    }
  }

  return std::make_tuple(sig_s, sig_a, sig_f);
}

std::tuple<double, double, double>
WindowedMultipole::evaluate_deriv(double E, double sqrtkT)
{
  // ==========================================================================
  // Bookkeeping

  // Define some frequently used variables.
  double sqrtE = std::sqrt(E);
  double invE = 1.0 / E;
  double T = sqrtkT*sqrtkT / K_BOLTZMANN;

  if (sqrtkT == 0.0) {
    fatal_error("Windowed multipole temperature derivatives are not implemented"
      " for 0 Kelvin cross sections.");
  }

  // Locate us
  int i_window = (sqrtE - std::sqrt(E_min_)) / spacing_;
  int startw = windows_(i_window, 0) - 1;
  int endw = windows_(i_window, 1) - 1;

  // Initialize the ouptut cross sections.
  double sig_s = 0.0;
  double sig_a = 0.0;
  double sig_f = 0.0;

  // TODO Polynomials: Some of the curvefit polynomials Doppler broaden so
  // rigorously we should be computing the derivative of those.  But in
  // practice, those derivatives are only large at very low energy and they
  // have no effect on reactor calculations.

  // ==========================================================================
  // Add the contribution from the poles in this window.

  double dopp = sqrt_awr_ / sqrtkT;
  if (endw >= startw) {
    for (int i_pole = startw; i_pole <= endw; ++i_pole) {
      std::complex<double> z = (sqrtE - data_(i_pole, MP_EA)) * dopp;
      std::complex<double> w_val = -invE * SQRT_PI * 0.5 * w_derivative(z, 2);
      sig_s += (data_(i_pole, MP_RS) * w_val).real();
      sig_a += (data_(i_pole, MP_RA) * w_val).real();
      if (fissionable_) {
        sig_f += (data_(i_pole, MP_RF) * w_val).real();
      }
    }
    sig_s *= -0.5*sqrt_awr_ / std::sqrt(K_BOLTZMANN) * std::pow(T, -1.5);
    sig_a *= -0.5*sqrt_awr_ / std::sqrt(K_BOLTZMANN) * std::pow(T, -1.5);
    sig_f *= -0.5*sqrt_awr_ / std::sqrt(K_BOLTZMANN) * std::pow(T, -1.5);
  }

  return std::make_tuple(sig_s, sig_a, sig_f);
}

//========================================================================
// Non-member functions
//========================================================================

void check_wmp_version(hid_t file)
{
  if (attribute_exists(file, "version")) {
    std::array<int, 2> version;
    read_attribute(file, "version", version);
    if (version[0] != WMP_VERSION[0]) {
      fatal_error(fmt::format(
        "WMP data format uses version {}.{} whereas your installation of "
        "OpenMC expects version {}.x data.",
        version[0], version[1], WMP_VERSION[0]));
    }
  } else {
    fatal_error(fmt::format("WMP data does not indicate a version. Your "
      "installation of OpenMC expects version {}x data.", WMP_VERSION[0]));
  }
}

void read_multipole_data(int i_nuclide)
{
  // Look for WMP data in cross_sections.xml
  const auto& nuc {data::nuclides[i_nuclide]};
  auto it = data::library_map.find({Library::Type::wmp, nuc->name_});

  // If no WMP library for this nuclide, just return
  if (it == data::library_map.end()) return;

  // Check if WMP library exists
  int idx = it->second;
  std::string& filename = data::libraries[idx].path_;

  // Display message
  write_message(6, "Reading {} WMP data from {}", nuc->name_, filename);

  // Open file and make sure version is sufficient
  hid_t file = file_open(filename, 'r');
  check_wmp_version(file);

  // Read nuclide data from HDF5
  hid_t group = open_group(file, nuc->name_.c_str());
  nuc->multipole_ = std::make_unique<WindowedMultipole>(group);
  close_group(group);
  file_close(file);
}

} // namespace openmc
<|endoftext|>