Camera calibration + recognition of QR code + solvepnp to solve pose

1. Camera calibration principle

In-depth science popularization: understand camera calibration in one article – Zhihu (zhihu.com)

2. Install the camera calibration package and start calibration

Now we know that the purpose of calibrating the camera is to obtain the internal parameter matrix and distortion coefficient, and to obtain the internal parameter matrix and distortion coefficient is to later use the function solvepnp that comes with opencv to solve the external parameter matrix.

First install the usb-cam function package

sudo apt install ros-noetic-usb-cam

Then install the camera calibration package that comes with ros

#noetic camera calibration tool installation
sudo apt-get install ros-noetic-camera-calibration

Start the camera and start calibration

#Start camera
roslaunch usb_cam usb_cam-test.launch
#Start calibration
rosrun camera_calibration cameracalibrator.py --size 8x6 --square 0.024 image:=/usb_cam/image_raw camera:=/usb_cam

Note before calibration:

It is recommended that you first look at the length and width of each frame obtained by opening the camera when processing the image, and then change the image obtained during calibration to this size. Because if the size of the processed image is different from the image obtained during calibration, the internal parameters will also be different.

How to change it specifically:

Find the file usb_cam-test.launch, usually under the path /opt/ros/noetic/share/usb_cam/launch. The original file is generally read-only. We can copy the contents and put it in a directory called my_usb_cam. -In the test.launch file, change the image_width and image_height parameters to the default sizes obtained when the camera is turned on. Here they are 1280 and 720.

Then put this launch file into the launch folder under a function package

For example, I put it in the opencv function package, create a new launch folder and put it in it.

At this time

#Start camera
roslaunch usb_cam usb_cam-test.launch

Change to:

#Start camera
roslaunch opencv my_usb_cam-test.launch
roslaunch package name launch file name

after that

#Start calibration
rosrun camera_calibration cameracalibrator.py --size 8x6 --square 0.024 image:=/usb_cam/image_raw camera:=/usb_cam

The parameters in the calibration command mean as follows:

1) size: Calibrate the number of internal corner points of the chessboard. The chessboard used here has a total number of rows, and each row has an internal corner point.
2) square: This parameter corresponds to the side length of each checkerboard, and the unit is meters.
3) image and camera: Set the image topic published by the camera.

Move the chessboard according to x (left and right), y (up and down), size (front and back), skew (tilt), etc., until the progress bars of x, y, size, and skew all turn to green positions.

If you feel it’s almost done, you can press CALIBRATE.

Just copy the internal parameters and distortion coefficients in the terminal and put them into a txt file you created yourself.

3. Installation of QR code recognition function package

QR code identifies the package that needs to be downloaded

  1. Install the ZBar library, here use apt installation instead of source code
 sudo apt install libzbar-dev

Using OpenCV-C++ and ZBar library in Ubuntu_Installing zbar on Raspberry Pi-CSDN blog

You can check whether your zbar is installed through the above blog.

4. opencv installation

How to install OpenCV on Ubuntu 20.04_ubuntu import cv2-CSDN Blog

You can try the first one first, and if you have any problems, try the following tutorials, or search for other tutorials yourself.

If you enter

pkg-config --modversion opencv4

Output:

4.2.0

This means that version 4.2.0 of opencv is successfully installed.

opencv is under /usr/share/opencv4; the header file is under /usr/include/opencv4, you can check it yourself.

5. Use of solvepnp and zbar (implementation of vscode + zbar + opencv + ros programming)

This is a function that comes with opencv, you can learn about it yourself. We want to calculate the external parameter matrix through this function.

OpenCV: solvePnP parameter introduction_cv::solvepnp-CSDN blog

You can see that the results of our previous camera calibration are used here, that is, the camera’s internal parameters and distortion coefficients.

The following is the code implementation of ros plus C++ plus opencv plus zbar

Create a function package “opencv” under the workspace. You can choose another name.

Create a new QR_ROS.cpp in the src directory of opencv. The code is as follows:

#include"ros/ros.h"
#include <iostream>
#include <opencv2/opencv.hpp>
#include <zbar.h>

using namespace std;
using namespace cv;
using namespace zbar;

//Camera internal parameter matrix
const Mat camera_matrix = (Mat_<double>(3, 3) << 970.59132483, 0, 646.87144122,0, 971.20780134, 427.7375449,0.00, 0.0, 1.0);
//Camera distortion coefficient
const Mat dist_coeffs = (Mat_<double>(5, 1) << -0.12721462, -1.10308713,0.045049, -0.01256439, 2.92627787);

int main(int argc, char *argv[]){
    ros::init(argc,argv,"QR_ROS");//ros node name cannot be repeated
    ros::NodeHandle nh;
    cv::VideoCapture cap(0); //Open the camera device, 0 represents the default camera

    if (!cap.isOpened()) {
        std::cerr << "Error: Couldn't open the camera." << std::endl;
        return 1;
    }

    // Initialize the ZBar scanner
    zbar::ImageScanner scanner;
    cv::Mat frame;
    cv::Mat frame_gray;
    cv::Scalar color = cv::Scalar(0, 255, 255);
    cv::Scalar color1 = cv::Scalar(0, 0, 255);
    cv::Scalar color2 = cv::Scalar(0, 255, 0);
    cv::Scalar color3 = cv::Scalar(255, 0, 0);
    cv::Scalar color4 = cv::Scalar(255, 255, 0);


    while (true) {
        //cv::Mat frame;
        cap >> frame; // Capture frame from camera

        // Convert OpenCV frame to grayscale
        cv::cvtColor(frame, frame_gray, cv::COLOR_BGR2GRAY);

        //Create a ZBar image
        zbar::Image zbar_image(frame_gray.cols, frame_gray.rows, "Y800", frame_gray.data, frame_gray.cols * frame_gray.rows);

        // Scan the image for barcodes
        int result = scanner.scan(zbar_image);

        // Iterate through the results
        for (zbar::Image::SymbolIterator symbol = zbar_image.symbol_begin(); symbol != zbar_image.symbol_end(); + + symbol) {
            std::cout << "Data: " << symbol->get_data() << std::endl;

            //Positioning information,
int x = symbol->get_location_x(0);
int y = symbol->get_location_y(0);
            cv::circle(frame, cv::Point(x,y), 15, color, 2, cv::LINE_8);
            //Calculate the width and height based on the pixel position of the polygon returned by zbar
            //The default QR code is vertical and horizontal
            int min_x = 0, min_y=0, max_x=0, max_y=0;
            Symbol::PointIterator pt = symbol->point_begin();
            Symbol::Point p = *pt;
            Symbol::PointIterator pt1 = symbol->point_begin();
            Symbol::Point p1 = *pt1;
            Symbol::PointIterator begin = symbol->point_begin();
            Symbol::Point begin_1 = *begin;
            
            
            //Draw a circle point by point and frame the QR code
             + + pt1;
            p1 = *pt1;
            //Draw a solid circle
            cv::circle(frame, cv::Point(p.x,p.y), 10, color1, 2, cv::LINE_8);
            cv::line(frame, cv::Point(p.x,p.y), cv::Point(p1.x, p1.y), color, 3);
             + + pt;
             + + pt1;
            p1 = *pt1;
            p = *pt;
            cv::circle(frame, cv::Point(p.x,p.y), 10, color2, 2, cv::LINE_8);
            cv::line(frame, cv::Point(p.x,p.y), cv::Point(p1.x, p1.y), color, 3);
             + + pt;
             + + pt1;
            p1 = *pt1;
            p = *pt;
            cv::circle(frame, cv::Point(p.x,p.y), 10, color3, 2, cv::LINE_8);
            cv::line(frame, cv::Point(p.x,p.y), cv::Point(p1.x, p1.y), color, 3);
             + + pt;
            p = *pt;
            cv::circle(frame, cv::Point(p.x,p.y), 10, color4, 2, cv::LINE_8);
            cv::line(frame, cv::Point(p.x,p.y), cv::Point(begin_1.x, begin_1.y), color, 3);
             
            // Counterclockwise starting from the upper left corner
            std::vector<Point2d> image_points;
            pt = symbol->point_begin();
            for (; pt != (Symbol::PointIterator)symbol->point_end(); + + pt) {
                p = *pt;
                image_points.push_back(Point2d(p.x, p.y));
            }

            // 3D feature point world coordinates, corresponding to pixel coordinates, unit is mm
            std::vector<Point3d> model_points;
            model_points.push_back(Point3d(-30.0, -30.0, 0.0)); // Upper left corner unit mm
            model_points.push_back(Point3d(-30.0, + 30.0, 0.0));
            model_points.push_back(Point3d( + 30.0, + 30.0, 0.0));
            model_points.push_back(Point3d( + 30.0, -30.0, 0.0));
            cout << "Camera Matrix " << endl << camera_matrix << endl << endl;
            // rotation vector
            Mat rotation_vector;
            // translation vector
            Mat translation_vector;

            // pnp solution
            solvePnP(model_points, image_points, camera_matrix, dist_coeffs, rotation_vector, translation_vector);
            //Default ITERATIVE method, you can try to modify it to EPNP (CV_EPNP), P3P (CV_P3P)

            cout << "Rotation Vector " << endl << rotation_vector << endl << endl;
            cout << "Translation Vector" << endl << translation_vector << endl << endl;


        }

        // Display the frame with detected barcodes
        cv::imshow("Barcode Scanner", frame);

        // Exit on 'q' key press
        if (cv::waitKey(1) == 'q') {
            break;
        }
    }

    //zbar_image.set_data(NULL, 0); //Clear cache

    return 0;
}

The directory structure is as follows, don’t worry about the open_camera.cpp

Write the CMake file (the one in the src directory, don’t make a mistake) as follows:

cmake_minimum_required(VERSION 3.0.2)
project(opencv)

## Compile as C++11, supported in ROS Kinetic and newer
# add_compile_options(-std=c++11)
#set(OpenCV_DIR /usr/share/opencv4/)
## Find catkin macros and libraries
## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz)
## is used, also find other catkin packages
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "/home/qly/VS_Project/Ros_Debug/src/opencv/cmake") #Write the absolute path of FindZBar
find_package(catkin REQUIRED COMPONENTS
  roscpp
  rospy
  std_msgs
)

find_package(OpenCV REQUIRED)
#set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake")
# Find the ZBar library in CMake
find_package(ZBar REQUIRED)
## System dependencies are found with CMake's conventions
# find_package(Boost REQUIRED COMPONENTS system)


## Uncomment this if the package has a setup.py. This macro ensures
## modules and global scripts declared therein get installed
## See http://ros.org/doc/api/catkin/html/user_guide/setup_dot_py.html
#catkin_python_setup()

###############################################
## Declare ROS messages, services and actions ##
###############################################

## To declare and build messages, services or actions from within this
## package, follow these steps:
## * Let MSG_DEP_SET be the set of packages whose message types you use in
## your messages/services/actions (e.g. std_msgs, actionlib_msgs, ...).
## * In the file package.xml:
## * add a build_depend tag for "message_generation"
## * add a build_depend and a exec_depend tag for each package in MSG_DEP_SET
## * If MSG_DEP_SET isn't empty the following dependency has been pulled in
## but can be declared for certainty nevertheless:
## * add a exec_depend tag for "message_runtime"
## * In this file (CMakeLists.txt):
## * add "message_generation" and every package in MSG_DEP_SET to
## find_package(catkin REQUIRED COMPONENTS ...)
## * add "message_runtime" and every package in MSG_DEP_SET to
## catkin_package(CATKIN_DEPENDS ...)
## * uncomment the add_*_files sections below as needed
## and list every .msg/.srv/.action file to be processed
## * uncomment the generate_messages entry below
## * add every package in MSG_DEP_SET to generate_messages(DEPENDENCIES ...)

## Generate messages in the 'msg' folder
# add_message_files(
#FILES
#Message1.msg
#Message2.msg
# )

## Generate services in the 'srv' folder
# add_service_files(
#FILES
#Service1.srv
#Service2.srv
# )

## Generate actions in the 'action' folder
# add_action_files(
#FILES
# Action1.action
# Action2.action
# )

## Generate added messages and services with any dependencies listed here
# generate_messages(
#DEPENDENCIES
# std_msgs
# )

###############################################
## Declare ROS dynamic reconfigure parameters ##
###############################################

## To declare and build dynamic reconfigure parameters within this
## package, follow these steps:
## * In the file package.xml:
## * add a build_depend and a exec_depend tag for "dynamic_reconfigure"
## * In this file (CMakeLists.txt):
## * add "dynamic_reconfigure" to
## find_package(catkin REQUIRED COMPONENTS ...)
## * uncomment the "generate_dynamic_reconfigure_options" section below
## and list every .cfg file to be processed

## Generate dynamic reconfigure parameters in the 'cfg' folder
# generate_dynamic_reconfigure_options(
#cfg/DynReconf1.cfg
#cfg/DynReconf2.cfg
# )

###################################
## catkin specific configuration ##
###################################
## The catkin_package macro generates cmake config files for your package
## Declare things to be passed to dependent projects
## INCLUDE_DIRS: uncomment this if your package contains header files
## LIBRARIES: libraries you create in this project that dependent projects also need
## 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 opencv
#CATKIN_DEPENDS roscpp rospy std_msgs
# DEPENDS system_lib
)

###########
## Build ##
###########

## Specify additional locations of header files
## Your package locations should be listed before other locations
include_directories(
#include
  ${catkin_INCLUDE_DIRS}
)

## Declare a C++ library
# add_library(${PROJECT_NAME}
# src/${PROJECT_NAME}/opencv.cpp
# )

## Add cmake target dependencies of the library
## as an example, code may need to be generated before libraries
## either from message generation or dynamic reconfigure
# add_dependencies(${PROJECT_NAME} ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})

## Declare a C++ executable
## With catkin_make all packages are built within a single CMake context
## The recommended prefix ensures that target names across packages don't collide
add_executable(open_camera src/open_camera.cpp)
add_executable(QR_ROS src/QR_ROS.cpp)

## Rename C++ executable without prefix
## The above recommended prefix causes long target names, the following renames the
## target back to the shorter version for ease of user use
## e.g. "rosrun someones_pkg node" instead of "rosrun someones_pkg someones_pkg_node"
# set_target_properties(${PROJECT_NAME}_node PROPERTIES OUTPUT_NAME node PREFIX "")

## Add cmake target dependencies of the executable
## same as for the library above
# add_dependencies(${PROJECT_NAME}_node ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})

## Specify libraries to link a library or executable target against
target_link_libraries(open_camera
${OpenCV_LIBS}
)
target_link_libraries(open_camera
  ${catkin_LIBRARIES}
)

target_link_libraries(QR_ROS
${OpenCV_LIBS}
${ZBAR_LIBRARIES}
)
target_link_libraries(QR_ROS
  ${catkin_LIBRARIES} # Without this, an error will be reported related to ros code
)






#############
## Install ##
#############

# all install targets should use catkin DESTINATION variables
# See http://ros.org/doc/api/catkin/html/adv_user_guide/variables.html

## Mark executable scripts (Python etc.) for installation
## in contrast to setup.py, you can choose the destination
# catkin_install_python(PROGRAMS
# scripts/my_python_script
# DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
# )

## Mark executables for installation
## See http://docs.ros.org/melodic/api/catkin/html/howto/format1/building_executables.html
# install(TARGETS ${PROJECT_NAME}_node
# RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
# )

## Mark libraries for installation
## See http://docs.ros.org/melodic/api/catkin/html/howto/format1/building_libraries.html
# install(TARGETS ${PROJECT_NAME}
# ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
# LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
# RUNTIME DESTINATION ${CATKIN_GLOBAL_BIN_DESTINATION}
# )

## Mark cpp header files for installation
# install(DIRECTORY include/${PROJECT_NAME}/
# DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}
# FILES_MATCHING PATTERN "*.h"
# PATTERN ".svn" EXCLUDE
# )

## Mark other files for installation (e.g. launch and bag files, etc.)
# install(FILES
# # myfile1
# # myfile2
# DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
# )

#############
## Testing ##
#############

## Add gtest based cpp test target and link libraries
# catkin_add_gtest(${PROJECT_NAME}-test test/test_opencv.cpp)
# if(TARGET ${PROJECT_NAME}-test)
# target_link_libraries(${PROJECT_NAME}-test ${PROJECT_NAME})
#endif()

## Add folders to be run by python nosetests
#catkin_add_nosetests(test)

In fact, these are the places that need to be added:

1. Set the absolute path of the FindZBar.cmake package. Here is my own path. Everyone needs to change it. This is to find the ZBAR package when compiling

set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "/home/qly/VS_Project/Ros_Debug/src/opencv/cmake") #Write the absolute path of FindZBar

2. Find the packages of opencv and zbar

find_package(OpenCV REQUIRED)
#set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake")
# Find the ZBar library in CMake
find_package(ZBar REQUIRED)

3. Add executable file

add_executable(QR_ROS src/QR_ROS.cpp)

4. Explain the libraries to be linked. The first one is to link Zbar and opencv. The second one should be to link libraries related to ros. Without this ros code, an error will be reported.

target_link_libraries(QR_ROS
${OpenCV_LIBS}
${ZBAR_LIBRARIES}
)
target_link_libraries(QR_ROS
  ${catkin_LIBRARIES} # Without this, an error will be reported related to ros code
)

Then compile and run it.