/ ****************************************************************************/
# -*- coding: utf-8 -*-
# This file is part of the Horus Project
__author__ = 'Jes煤s Arroyo Torrens <jesus.arroyo@bq.com>'
__copyright__ = 'Copyright (C) 2014-2016 Mundo Reader S.L.'
__license__ = 'GNU General Public License v2 http://www.gnu.org/licenses/gpl2.html'
import cv2
import numpy as np
from horus import Singleton
from horus.engine.calibration.pattern import Pattern
from horus.engine.calibration.calibration_data import CalibrationData
@Singleton
class ImageDetection(object):
def __init__(self):
self.pattern = Pattern()
self.calibration_data = CalibrationData()
self._criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
def detect_pattern(self, image):
corners = self._detect_chessboard(image)
if corners is not None:
image = self.draw_pattern(image, corners)
return image
def draw_pattern(self, image, corners):
image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
cv2.drawChessboardCorners(
image, (self.pattern.columns, self.pattern.rows), corners, True)
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
return image
def detect_corners(self, image):
corners = self._detect_chessboard(image)
return corners
def detect_pose(self, image):
corners = self._detect_chessboard(image)
if corners is not None:
ret, rvecs, tvecs = cv2.solvePnP(
self.pattern.object_points, corners,
self.calibration_data.camera_matrix, self.calibration_data.distortion_vector)
if ret:
return (cv2.Rodrigues(rvecs)[0], tvecs, corners)
def detect_pattern_plane(self, pose):
if pose is not None:
R = pose[0]
t = pose[1].T[0]
c = pose[2]
n = R.T[2]
d = np.dot(n, t)
return (d, n, c)
def pattern_mask(self, image, corners):
if image is not None:
h, w, d = image.shape
if corners is not None:
corners = corners.astype(np.int)
p1 = corners[0][0]
p2 = corners[self.pattern.columns - 1][0]
p3 = corners[self.pattern.columns * (self.pattern.rows - 1)][0]
p4 = corners[self.pattern.columns * self.pattern.rows - 1][0]
mask = np.zeros((h, w), np.uint8)
points = np.array([p1, p2, p4, p3])
cv2.fillConvexPoly(mask, points, 255)
image = cv2.bitwise_and(image, image, mask=mask)
return image
def _detect_chessboard(self, image):
if image is not None:
if self.pattern.rows > 2 and self.pattern.columns > 2:
gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
ret, corners = cv2.findChessboardCorners(
gray, (self.pattern.columns, self.pattern.rows), flags=cv2.CALIB_CB_FAST_CHECK)
if ret:
cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), self._criteria)
return corners
# -*- coding: utf-8 -*-
# This file is part of the Horus Project
__author__ = 'Jes煤s Arroyo Torrens <jesus.arroyo@bq.com>'
__copyright__ = 'Copyright (C) 2014-2016 Mundo Reader S.L.'
__license__ = 'GNU General Public License v2 http://www.gnu.org/licenses/gpl2.html'
import cv2##导入OPENCV
import numpy as np
##numpy是PYTHON中的一个数值计算库,非常有用。
from horus import Singleton
from horus.engine.calibration.pattern import Pattern##这是一个校正的模式,
##这个模式的具体含义可以在工程文件中找到,包括行、列等的设置
from horus.engine.calibration.calibration_data import CalibrationData#相机内
##内参校正。
@Singleton
class ImageDetection(object):
def __init__(self):
self.pattern = Pattern()
self.calibration_data = CalibrationData()
self._criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
##cv2.TERM_CRITERIA_EPS :精确度(误差)满足epsilon停止。 cv2.TERM_CRITERIA_MAX_ITER:迭代次数超过max_iter停止 cv2.TERM_CRITERIA_EPS+cv2.TERM_CRITERIA_MAX_ITER,两者合体,任意一个满足结束。
##self.criteria这代表了一个聚类算法,关于聚类算法有需要了解的同学可以自行搜索。
def detect_pattern(self, image):
corners = self._detect_chessboard(image)##这里是对棋盘的角点检测,也是就校正。如果角点非空,则进行画线。
if corners is not None:
image = self.draw_pattern(image, corners)##这是画线的模式,具体画线的模式,要参考draw_pattern这个函数,后面会讲到。
return image
def draw_pattern(self, image, corners):##
image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)##Python: cv2.cvtColor(src, code[, dst[, dstCn]]) → dst
##cv2.cvtColor(image, cv2.COLOR_RGB2BGR)这是OPENCV的图像空间的转换。
cv2.drawChessboardCorners(
image, (self.pattern.columns, self.pattern.rows), corners, True)## cv2.drawChessboardCorners()将角点绘制到图像上显示
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
return image
##我们使用cv2.calibrateCamera()进行标定,这个函数会返回标定结果、相机的内参数矩阵、畸变系数、旋转矩阵和平移向量。
def detect_corners(self, image):
corners = self._detect_chessboard(image)
return corners
def detect_pose(self, image):
corners = self._detect_chessboard(image)
if corners is not None:
ret, rvecs, tvecs = cv2.solvePnP(##利用这个函数来根据一些已知点对求相机在空间坐标系中的旋转及偏移。
self.pattern.object_points, corners,
self.calibration_data.camera_matrix, self.calibration_data.distortion_vector)
if ret:
return (cv2.Rodrigues(rvecs)[0], tvecs, corners)
##OpenCV中有solvePnP以及solvePnPRansac用来实现已知平面四点坐标确定摄像头相对世界坐标系的平移和旋转。cvPOSIT基于正交投影,用仿射投影模型近似透视投影模型,不断迭代计算出估计值。此算法在物体深度相对于物体到相机的距离比较大的时候,算法可能不收敛。
从世界坐标系到相机坐标系的转换,需要矩阵[R|t],其中R是旋转矩阵,t是位移向量。如果世界坐标系为X,相机坐标系对应坐标为X‘,那么X' = [R|t]*X。从相机坐标系到理想屏幕坐标系的变换就需要内参数矩阵C。那么理想屏幕坐标系L = C*[R|t]*X。如何获得[R|t],大致是已知模板上的几个关键点在世界坐标系的坐标即X已知,然后在摄像头捕获的帧里获得模板上对应点在屏幕坐标系的坐标即L已知,通过求解线性方程组得到[R|t]的初值,再利用非线性最小二乘法迭代求得最优变换矩阵[R|t]。
大多数情况下,背景是二维平面,识别的物体也是二维平面。对于ARToolkit,识别的Targets就是平面的(但是这种方法鲁棒性不好)。如果内参数矩阵是已知的,那么知道4个或者更多共面不共线的点就可以计算出相机的姿态。
相机姿态估计的问题就是寻找相机的外参数,即是最小化误差函数的问题。误差函数有的基于image-space,有的基于object-space。
def detect_pattern_plane(self, pose):
if pose is not None:
R = pose[0]
t = pose[1].T[0]
c = pose[2]##整个算法的作用就是从世界坐标系转换到相机内部的屏幕坐标系。这要有很多算法知识,请自行查找。
n = R.T[2]
d = np.dot(n, t)
return (d, n, c)
def pattern_mask(self, image, corners):##模式掩膜操作,这里就是提取ROI,我自己的理解就是想要提取出激光束的那些切片,用来建立点云数据。
if image is not None:
h, w, d = image.shape##了解掩膜,看这里http://blog.csdn.net/zouyu1746430162/article/details/78762383
if corners is not None:
corners = corners.astype(np.int)
p1 = corners[0][0]
p2 = corners[self.pattern.columns - 1][0]
p3 = corners[self.pattern.columns * (self.pattern.rows - 1)][0]
p4 = corners[self.pattern.columns * self.pattern.rows - 1][0]
mask = np.zeros((h, w), np.uint8)
points = np.array([p1, p2, p4, p3])
cv2.fillConvexPoly(mask, points, 255)##cv2.fillPoly 和 cv2.fillConvexPoly:非凸任意形状填充和凸填充http://blog.csdn.net/xuyangcao123/article/details/72780693
image = cv2.bitwise_and(image, image, mask=mask)
return image
##bitwise_and是对二进制数据进行“与”操作,即对图像(灰度图像或彩色图像均可)每个像素值进行二进制“与”操作,1&1=1,1&0=0,0&1=0,0&0=0
def _detect_chessboard(self, image):
if image is not None:
if self.pattern.rows > 2 and self.pattern.columns > 2:
gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
ret, corners = cv2.findChessboardCorners(
gray, (self.pattern.columns, self.pattern.rows), flags=cv2.CALIB_CB_FAST_CHECK)
if ret:
cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), self._criteria)
return corners
(1)函数原型/*cornerSubPix()函数
cornerSubPix()函数在角点检测中精确化角点位置,其函数原型如下:
C++: void cornerSubPix(InputArray image, InputOutputArray corners, Size winSize, Size zeroZone, TermCriteria criteria);
C: void cvFindCornerSubPix(const CvArr* image, CvPoint2D32f* corners, int count, CvSize win, CvSize zero_zone, CvTermCriteria criteria);
(2)函数参数
函数参数说明如下:
image:输入图像
corners:输入角点的初始坐标以及精准化后的坐标用于输出。
winSize:搜索窗口边长的一半,例如如果winSize=Size(5,5),则一个大小为的搜索窗口将被使用。
zeroZone:搜索区域中间的dead region边长的一半,有时用于避免自相关矩阵的奇异性。如果值设为(-1,-1)则表示没有这个区域。
criteria:角点精准化迭代过程的终止条件。也就是当迭代次数超过criteria.maxCount,或者角点位置变化小于criteria.epsilon时,停止迭代过程。