mirror of
https://github.com/Jieshoudaxue/ros_senior.git
synced 2026-08-29 16:40:44 +08:00
Merge pull request #11 from Jieshoudaxue/dev
[feat] add qr_detector in robot_vison
This commit is contained in:
@@ -1,8 +0,0 @@
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Changelog for package qr_detector
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
1.0.0 (2017-05-01)
|
||||
------------------
|
||||
* QR detection wrapper on zbar implemented
|
||||
* Contributors: Michal Drwiega
|
||||
@@ -1,37 +0,0 @@
|
||||
cmake_minimum_required(VERSION 2.8.11)
|
||||
project(qr_detector)
|
||||
|
||||
set(CMAKE_BUILD_TYPE Release)
|
||||
set(CMAKE_CXX_FLAGS "-std=c++11 -Wall ${CMAKE_CXX_FLAGS}")
|
||||
|
||||
find_package(catkin REQUIRED COMPONENTS
|
||||
roscpp nodelet sensor_msgs cv_bridge image_transport)
|
||||
|
||||
catkin_package(
|
||||
INCLUDE_DIRS include
|
||||
LIBRARIES qr_detector_nodelet
|
||||
CATKIN_DEPENDS roscpp nodelet sensor_msgs cv_bridge image_transport
|
||||
)
|
||||
|
||||
find_library(ZBAR_LIBRARIES NAMES zbar)
|
||||
|
||||
include_directories(include ${catkin_INCLUDE_DIRS})
|
||||
|
||||
add_library(qr_detector_nodelet src/qr_detector_nodelet.cpp src/detector.cpp)
|
||||
target_link_libraries(qr_detector_nodelet zbar ${catkin_LIBRARIES})
|
||||
|
||||
install(TARGETS qr_detector_nodelet
|
||||
ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
|
||||
LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
|
||||
RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
|
||||
)
|
||||
|
||||
install(DIRECTORY include/${PROJECT_NAME}/
|
||||
DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION})
|
||||
|
||||
install(DIRECTORY launch
|
||||
DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION})
|
||||
|
||||
install(FILES nodelets.xml
|
||||
DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
|
||||
)
|
||||
@@ -1,27 +0,0 @@
|
||||
* Software License Agreement (BSD License)
|
||||
*
|
||||
* Copyright (c) 2017-, Michal Drwiega (drwiega.michal@gmail.com)
|
||||
* 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.
|
||||
* 3. Neither the name of the copyright holder 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.
|
||||
@@ -1,11 +0,0 @@
|
||||
## qr_detector
|
||||
The QR codes detector based on zbar library (http://zbar.sourceforge.net), dedicated to ROS systems.
|
||||
|
||||
Installation of zbar library on Ubuntu
|
||||
`sudo apt install libzbar-dev`
|
||||
|
||||
Subscribes:
|
||||
- **/image** (sensor_msgs/Image) - the topic with RGB images which contains QR codes.
|
||||
|
||||
Publishes:
|
||||
- **/qr_codes** (std_msgs/String) - message from each detected QR code is published as a string.
|
||||
@@ -1,37 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <opencv2/opencv.hpp>
|
||||
#include "zbar.h"
|
||||
|
||||
namespace qr_detector {
|
||||
|
||||
struct Tag {
|
||||
std::string message;
|
||||
std::vector<cv::Point> polygon;
|
||||
};
|
||||
|
||||
using Tags = std::vector<Tag>;
|
||||
|
||||
/**
|
||||
* Main QR codes detector class
|
||||
*/
|
||||
class Detector {
|
||||
public:
|
||||
Detector();
|
||||
~Detector() = default;
|
||||
|
||||
/**
|
||||
* Detects tags in image.
|
||||
*
|
||||
* @param[in] image - image with qr tags
|
||||
* @param[in] timeout - max time for tags detection in ms
|
||||
*/
|
||||
Tags detect(const cv::Mat& image, size_t timeout = 100);
|
||||
|
||||
protected:
|
||||
zbar::ImageScanner scanner_;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <ros/ros.h>
|
||||
#include <nodelet/nodelet.h>
|
||||
#include <image_transport/image_transport.h>
|
||||
|
||||
#include "qr_detector/detector.h"
|
||||
|
||||
namespace qr_detector {
|
||||
|
||||
class QrDetectorNodelet : public nodelet::Nodelet {
|
||||
public:
|
||||
QrDetectorNodelet();
|
||||
virtual ~QrDetectorNodelet();
|
||||
|
||||
private:
|
||||
void onInit() override;
|
||||
void connectCallback();
|
||||
void disconnectCallback();
|
||||
void imageCallback(const sensor_msgs::ImageConstPtr& image);
|
||||
|
||||
ros::NodeHandle nh_;
|
||||
image_transport::ImageTransport it_;
|
||||
image_transport::Subscriber img_subscriber_;
|
||||
ros::Publisher tags_publisher_;
|
||||
Detector detector_;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
<package format="2">
|
||||
<name>qr_detector</name>
|
||||
<version>1.0.0</version>
|
||||
<description>
|
||||
QR codes detector based on zbar library (http://zbar.sourceforge.net)
|
||||
</description>
|
||||
<author email="drwiega.michal@gmail.com">Michal Drwiega (http://www.mdrwiega.com)</author>
|
||||
<maintainer email="drwiega.michal@gmail.com">Michal Drwiega</maintainer>
|
||||
<license>BSD</license>
|
||||
<url type="website">http://ros.org/wiki/qr_detector</url>
|
||||
|
||||
<buildtool_depend>catkin</buildtool_depend>
|
||||
|
||||
<depend>roscpp</depend>
|
||||
<depend>nodelet</depend>
|
||||
<depend>zbar</depend>
|
||||
<depend>sensor_msgs</depend>
|
||||
<depend>cv_bridge</depend>
|
||||
<depend>image_transport</depend>
|
||||
|
||||
<export>
|
||||
<nodelet plugin="${prefix}/nodelets.xml"/>
|
||||
</export>
|
||||
</package>
|
||||
@@ -1,38 +0,0 @@
|
||||
#include "qr_detector/detector.h"
|
||||
|
||||
#include <cv_bridge/cv_bridge.h>
|
||||
|
||||
namespace qr_detector {
|
||||
|
||||
Detector::Detector() : scanner_()
|
||||
{
|
||||
scanner_.set_config(zbar::ZBAR_NONE, zbar::ZBAR_CFG_ENABLE, 1);
|
||||
}
|
||||
|
||||
Tags Detector::detect(const cv::Mat& image, size_t timeout)
|
||||
{
|
||||
cv::Mat grayImg;
|
||||
cv::cvtColor(image, grayImg, CV_BGR2GRAY);
|
||||
|
||||
const auto width = image.cols;
|
||||
const auto height = image.rows;
|
||||
|
||||
zbar::Image img(width, height, "Y800", grayImg.data, width * height);
|
||||
scanner_.scan(img);
|
||||
|
||||
Tags tags;
|
||||
for (auto s = img.symbol_begin(); s != img.symbol_end(); ++s)
|
||||
{
|
||||
Tag tag;
|
||||
tag.message = s->get_data();
|
||||
|
||||
for(int i = 0; i < s->get_location_size(); i++) {
|
||||
tag.polygon.push_back(cv::Point(s->get_location_x(i), s->get_location_y(i)));
|
||||
}
|
||||
tags.push_back(tag);
|
||||
}
|
||||
|
||||
return tags;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
#include "qr_detector/qr_detector_nodelet.h"
|
||||
|
||||
#include "pluginlib/class_list_macros.h"
|
||||
#include <cv_bridge/cv_bridge.h>
|
||||
#include <sensor_msgs/image_encodings.h>
|
||||
#include "std_msgs/String.h"
|
||||
|
||||
PLUGINLIB_EXPORT_CLASS(qr_detector::QrDetectorNodelet, nodelet::Nodelet);
|
||||
|
||||
namespace qr_detector {
|
||||
|
||||
QrDetectorNodelet::QrDetectorNodelet()
|
||||
: it_(nh_)
|
||||
{ }
|
||||
|
||||
QrDetectorNodelet::~QrDetectorNodelet()
|
||||
{
|
||||
img_subscriber_.shutdown();
|
||||
}
|
||||
|
||||
void QrDetectorNodelet::onInit()
|
||||
{
|
||||
nh_ = getNodeHandle();
|
||||
|
||||
tags_publisher_ = nh_.advertise<std_msgs::String>("qr_codes", 10,
|
||||
std::bind(&QrDetectorNodelet::connectCallback, this),
|
||||
std::bind(&QrDetectorNodelet::disconnectCallback, this));
|
||||
|
||||
NODELET_INFO_STREAM("Initializing nodelet... [" << nh_.getNamespace() << "]");
|
||||
}
|
||||
|
||||
void QrDetectorNodelet::connectCallback()
|
||||
{
|
||||
if (!img_subscriber_ && tags_publisher_.getNumSubscribers() > 0)
|
||||
{
|
||||
NODELET_INFO("Connecting to image topic.");
|
||||
img_subscriber_ = it_.subscribe("image", 1, &QrDetectorNodelet::imageCallback, this);
|
||||
}
|
||||
}
|
||||
|
||||
void QrDetectorNodelet::disconnectCallback()
|
||||
{
|
||||
if (tags_publisher_.getNumSubscribers() == 0)
|
||||
{
|
||||
NODELET_INFO("Unsubscribing from image topic.");
|
||||
img_subscriber_.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
void QrDetectorNodelet::imageCallback(const sensor_msgs::ImageConstPtr &image)
|
||||
{
|
||||
cv_bridge::CvImageConstPtr cv_image;
|
||||
|
||||
try {
|
||||
cv_image = cv_bridge::toCvShare(image, sensor_msgs::image_encodings::BGR8);
|
||||
}
|
||||
catch (cv_bridge::Exception& e) {
|
||||
ROS_ERROR("cv_bridge exception: %s", e.what());
|
||||
return;
|
||||
}
|
||||
|
||||
auto tags = detector_.detect(cv_image->image, 10);
|
||||
for (auto& tag : tags)
|
||||
{
|
||||
std_msgs::String msg;
|
||||
msg.data = tag.message;
|
||||
tags_publisher_.publish(msg);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,6 +3,7 @@ project(robot_vision)
|
||||
|
||||
## Compile as C++11, supported in ROS Kinetic and newer
|
||||
# add_compile_options(-std=c++11)
|
||||
set(CMAKE_CXX_FLAGS "-std=c++11 -Wall ${CMAKE_CXX_FLAGS}")
|
||||
|
||||
## Find catkin macros and libraries
|
||||
## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz)
|
||||
@@ -11,6 +12,9 @@ find_package(catkin REQUIRED COMPONENTS
|
||||
cv_bridge
|
||||
image_transport
|
||||
roscpp
|
||||
nodelet
|
||||
cv_bridge
|
||||
image_transport
|
||||
rospy
|
||||
sensor_msgs
|
||||
std_msgs
|
||||
@@ -108,10 +112,9 @@ generate_messages(
|
||||
## CATKIN_DEPENDS: catkin_packages dependent projects also need
|
||||
## DEPENDS: system dependencies of this project that dependent projects also need
|
||||
catkin_package(
|
||||
# INCLUDE_DIRS include
|
||||
# LIBRARIES robot_vision
|
||||
# CATKIN_DEPENDS cv_bridge image_transport roscpp rospy sensor_msgs std_msgs
|
||||
# DEPENDS system_lib
|
||||
INCLUDE_DIRS include
|
||||
LIBRARIES qr_detector_nodelet
|
||||
CATKIN_DEPENDS roscpp nodelet sensor_msgs cv_bridge image_transport
|
||||
)
|
||||
|
||||
###########
|
||||
@@ -120,8 +123,10 @@ catkin_package(
|
||||
|
||||
## Specify additional locations of header files
|
||||
## Your package locations should be listed before other locations
|
||||
find_library(ZBAR_LIBRARIES NAMES zbar)
|
||||
|
||||
include_directories(
|
||||
# include
|
||||
include
|
||||
${catkin_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
@@ -129,6 +134,8 @@ include_directories(
|
||||
# add_library(${PROJECT_NAME}
|
||||
# src/${PROJECT_NAME}/robot_vision.cpp
|
||||
# )
|
||||
add_library(qr_detector_nodelet src/qr_detector_nodelet.cpp)
|
||||
|
||||
|
||||
## Add cmake target dependencies of the library
|
||||
## as an example, code may need to be generated before libraries
|
||||
@@ -154,6 +161,7 @@ include_directories(
|
||||
# target_link_libraries(${PROJECT_NAME}_node
|
||||
# ${catkin_LIBRARIES}
|
||||
# )
|
||||
target_link_libraries(qr_detector_nodelet zbar ${catkin_LIBRARIES})
|
||||
|
||||
#############
|
||||
## Install ##
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,81 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <opencv2/opencv.hpp>
|
||||
#include <ros/ros.h>
|
||||
#include <nodelet/nodelet.h>
|
||||
#include <cv_bridge/cv_bridge.h>
|
||||
#include <image_transport/image_transport.h>
|
||||
#include <zbar.h>
|
||||
|
||||
namespace qr_detector {
|
||||
|
||||
struct QrTag {
|
||||
std::string qr_msg;
|
||||
std::vector<cv::Point> square_apex_vector;
|
||||
};
|
||||
|
||||
using QrTags = std::vector<QrTag>;
|
||||
|
||||
class QrDetector {
|
||||
public:
|
||||
QrDetector() {
|
||||
scanner_.set_config(zbar::ZBAR_NONE, zbar::ZBAR_CFG_ENABLE, 1);
|
||||
}
|
||||
|
||||
QrTags detect(const cv::Mat& image) {
|
||||
cv::Mat gray_img;
|
||||
cv::cvtColor(image, gray_img, CV_BGR2GRAY);
|
||||
|
||||
const uint64_t width = image.cols;
|
||||
const uint64_t height = image.rows;
|
||||
zbar::Image img(width, height, "Y800", gray_img.data, width*height);
|
||||
scanner_.scan(img);
|
||||
|
||||
QrTags tags;
|
||||
for (auto s = img.symbol_begin(); s != img.symbol_end(); ++s) {
|
||||
QrTag tag;
|
||||
tag.qr_msg = s->get_data();
|
||||
for (int i = 0; i < s->get_location_size(); i++) {
|
||||
tag.square_apex_vector.push_back(cv::Point(s->get_location_x(i), s->get_location_y(i)));
|
||||
}
|
||||
|
||||
tags.push_back(tag);
|
||||
}
|
||||
|
||||
return tags;
|
||||
}
|
||||
|
||||
private:
|
||||
zbar::ImageScanner scanner_;
|
||||
};
|
||||
|
||||
class QrDetectorNodelet : public nodelet::Nodelet {
|
||||
public:
|
||||
QrDetectorNodelet();
|
||||
virtual ~QrDetectorNodelet();
|
||||
|
||||
void onInit() override;
|
||||
|
||||
private:
|
||||
void DownstreamConnectCb();
|
||||
void DownstreamDisconnectCb();
|
||||
void ImageCb(const sensor_msgs::ImageConstPtr& image);
|
||||
|
||||
private:
|
||||
ros::NodeHandle nh_;
|
||||
image_transport::ImageTransport it_;
|
||||
image_transport::Subscriber img_sub_;
|
||||
ros::Publisher qr_pub_;
|
||||
QrDetector detector_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
<node pkg="nodelet"
|
||||
type="nodelet"
|
||||
name="qr_detector"
|
||||
args="standalone qr_detector/qr_detector_nodelet">
|
||||
output="screen"
|
||||
args="standalone robot_vision/qr_detector_nodelet">
|
||||
<remap from="image" to="usb_cam/image_raw"/>
|
||||
</node>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<library path="lib/libqr_detector_nodelet">
|
||||
|
||||
<class name="qr_detector/qr_detector_nodelet"
|
||||
<class name="robot_vision/qr_detector_nodelet"
|
||||
type="qr_detector::QrDetectorNodelet"
|
||||
base_class_type="nodelet::Nodelet">
|
||||
<description>
|
||||
@@ -52,26 +52,32 @@
|
||||
<build_depend>cv_bridge</build_depend>
|
||||
<build_depend>image_transport</build_depend>
|
||||
<build_depend>roscpp</build_depend>
|
||||
<build_depend>nodelet</build_depend>
|
||||
<build_depend>zbar</build_depend>
|
||||
<build_depend>image_transport</build_depend>
|
||||
<build_depend>rospy</build_depend>
|
||||
<build_depend>sensor_msgs</build_depend>
|
||||
<build_depend>std_msgs</build_depend>
|
||||
<build_export_depend>cv_bridge</build_export_depend>
|
||||
<build_export_depend>image_transport</build_export_depend>
|
||||
<build_export_depend>roscpp</build_export_depend>
|
||||
<build_export_depend>nodelet</build_export_depend>
|
||||
<build_export_depend>zbar</build_export_depend>
|
||||
<build_export_depend>image_transport</build_export_depend>
|
||||
<build_export_depend>rospy</build_export_depend>
|
||||
<build_export_depend>sensor_msgs</build_export_depend>
|
||||
<build_export_depend>std_msgs</build_export_depend>
|
||||
<exec_depend>cv_bridge</exec_depend>
|
||||
<exec_depend>image_transport</exec_depend>
|
||||
<exec_depend>roscpp</exec_depend>
|
||||
<exec_depend>nodelet</exec_depend>
|
||||
<exec_depend>zbar</exec_depend>
|
||||
<exec_depend>image_transport</exec_depend>
|
||||
<exec_depend>rospy</exec_depend>
|
||||
<exec_depend>sensor_msgs</exec_depend>
|
||||
<exec_depend>std_msgs</exec_depend>
|
||||
|
||||
|
||||
<!-- The export tag contains other, unspecified, tags -->
|
||||
<export>
|
||||
<!-- Other tools can request additional information be placed here -->
|
||||
|
||||
<nodelet plugin="${prefix}/nodelets.xml"/>
|
||||
</export>
|
||||
</package>
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
#include "qr_detector/qr_detector_nodelet.h"
|
||||
|
||||
#include <pluginlib/class_list_macros.h>
|
||||
#include <cv_bridge/cv_bridge.h>
|
||||
#include <sensor_msgs/image_encodings.h>
|
||||
#include <std_msgs/String.h>
|
||||
|
||||
PLUGINLIB_EXPORT_CLASS(qr_detector::QrDetectorNodelet, nodelet::Nodelet);
|
||||
|
||||
namespace qr_detector {
|
||||
|
||||
QrDetectorNodelet::QrDetectorNodelet() : it_(nh_) {
|
||||
ROS_INFO("QrDetectorNodelet Constructor");
|
||||
}
|
||||
|
||||
QrDetectorNodelet::~QrDetectorNodelet() {
|
||||
img_sub_.shutdown();
|
||||
ROS_INFO("QrDetectorNodelet Destructor");
|
||||
}
|
||||
|
||||
void QrDetectorNodelet::onInit() {
|
||||
nh_ = getNodeHandle();
|
||||
qr_pub_ = nh_.advertise<std_msgs::String>("/qr_codes", 10,
|
||||
std::bind(&QrDetectorNodelet::DownstreamConnectCb, this),
|
||||
std::bind(&QrDetectorNodelet::DownstreamDisconnectCb, this));
|
||||
ROS_INFO("init qr detector nodelet, nodehand name %s", nh_.getNamespace().c_str());
|
||||
}
|
||||
|
||||
void QrDetectorNodelet::DownstreamConnectCb() {
|
||||
if (!img_sub_ && qr_pub_.getNumSubscribers() > 0) {
|
||||
img_sub_ = it_.subscribe("image", 1, &QrDetectorNodelet::ImageCb, this);
|
||||
ROS_INFO("subscribe image topic from usb_cam");
|
||||
}
|
||||
}
|
||||
|
||||
void QrDetectorNodelet::DownstreamDisconnectCb() {
|
||||
if (qr_pub_.getNumSubscribers() == 0) {
|
||||
img_sub_.shutdown();
|
||||
ROS_INFO("unsubscibe image topic");
|
||||
}
|
||||
}
|
||||
|
||||
void QrDetectorNodelet::ImageCb(const sensor_msgs::ImageConstPtr& image) {
|
||||
cv_bridge::CvImageConstPtr cv_image;
|
||||
|
||||
try {
|
||||
cv_image = cv_bridge::toCvShare(image, sensor_msgs::image_encodings::BGR8);
|
||||
} catch (cv_bridge::Exception& e) {
|
||||
ROS_ERROR("cv_bridge exception %s", e.what());
|
||||
return;
|
||||
}
|
||||
|
||||
QrTags qr_tags = detector_.detect(cv_image->image);
|
||||
for (auto& qr_tag : qr_tags) {
|
||||
std_msgs::String msg;
|
||||
msg.data = qr_tag.qr_msg;
|
||||
|
||||
qr_pub_.publish(msg);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user