Open CV 识别指针偏转角度

 在学习一段代码的时候 有段代码阅读的不是很懂 该段代码的目的是通过旋转虚拟指针找到和原指针重合最高的地方 并求出角度

t1 = img.copy(); t1[temp[:, :, 2] == 255] = 255; c = img[temp[:, :, 2] == 255]; points = c[c == 0]; freq_list.append((len(points), i))

这五行代码就十分疑惑不知所云 不知道各位朋友能不能详细解惑一番

def get_pointer_rad(img):
    shape = img.shape
    c_y, c_x, depth = int(shape[0] / 2), int(shape[1] / 2), shape[2]
    x1=c_x+c_x*0.8
src = img.copy()
    freq_list = []
    for i in range(361):
        x = (x1 - c_x) * cos(i * pi / 180) + c_x
        y = (x1 - c_x) * sin(i * pi / 180) + c_y
        temp = src.copy()
        cv2.line(temp, (c_x, c_y), (int(x), int(y)), (0, 0, 255), thickness=3)
        t1 = img.copy()
        t1[temp[:, :, 2] == 255] = 255
        c = img[temp[:, :, 2] == 255]
        points = c[c == 0]
        freq_list.append((len(points), i))
        cv2.imshow('d', temp)
        cv2.imshow('d1', t1)
        cv2.waitKey(1)
    print('当前角度:',max(freq_list, key=lambda x: x[0]),'度')
    cv2.destroyAllWindows()
return max(freq_list, key=lambda x: x[0])

 

你这个格式确定没问题吗?对齐了很好理解了。前面几行看图应该就能理解是干什么的,从11开始,就是在temp中画一条(c_x,c_y)到点(x,y)的红色的直线,13行是判断temp中,如果某个点的红色通道灰度值为255,则将t1中同样位置的该点的颜色置为255(白色),14行c是将temp中红色通道为白色(255)的位置置为True,其他为false,15行是将C中等于0的点位置坐标传给points(确定这里没错吗?应该是c==True才能符合预期效果吧),
然后freq_list 保存points的长度和此时的角度i。最后几行的显示图像,20和22的max()是找到freq_list[0]中最大的点。

综合起来说,就是用一个旋转的直线,来判断图中和直线重合的点最多的直线的旋转角度是多少,及最后的重合点的个数(15行要修改才行),像下面的那个图,最后的结果就是重合点最多的是3261个,旋转角是319度


import cv2
from math import cos,sin,pi
import numpy as np

def get_pointer_rad(img):
    shape = img.shape
    c_y, c_x, depth = int(shape[0] / 2), int(shape[1] / 2), shape[2]
    x1=c_x+c_x*0.8
    src = img.copy()
    freq_list = []
    for i in range(361):
        x = (x1 - c_x) * cos(i * pi / 180) + c_x
        y = (x1 - c_x) * sin(i * pi / 180) + c_y
        temp = src.copy()
        cv2.line(temp, (c_x, c_y), (int(x), int(y)), (0, 0, 255), thickness=3)
        t1 = img.copy()
        t1[temp[:, :, 2] == 255] = [0,0,255]
        c = img[temp[:, :, 2] == 255]
        points = c[c == True]
        freq_list.append((len(points), i))
        cv2.imshow('d', temp)
        cv2.imshow('d1', t1)

        cv2.waitKey(1)
    print('当前角度:',max(freq_list, key=lambda x: x[0]),'度')
    cv2.destroyAllWindows()
    return max(freq_list, key=lambda x: x[0])

img=np.ones((500,500),np.uint8)
img=cv2.cvtColor(img,cv2.COLOR_GRAY2BGR)
cv2.line(img,(250,250),(20,20),(0,0,255),3,8)
get_pointer_rad(img)


#output
当前角度: (3261, 319) 度

 

img是一个RGB图像的三维数组,其上有一个黑色的指针,指针旋转的圆心在图像中心。要想知道这个指针的角度,就在img的一个副本temp上以图像中心为圆心,从0°~360°循环,做以下工作:画蓝色线条,找出temp上蓝点的位置(temp[:, :, 2] == 255),得到img上对应位置的像素中蓝色通道的数值组成的数组c,数组c中等于0的元素组成points,将points长度和当前的角度存到列表(freq_list)中。很显然,如果在某个角度画出的蓝色线条正好和指针重合,那么points中0的个数一定最多,因为指针是黑色的,指针之外的像素都不是黑色的。

如果img是BGR模式,那么上面描述中的蓝色都变成红色。

不过,这个代码写得不好:

  1. 不该from numpy import *,而应该import numpy as np
  2. 无需自己使用i*pi/180计算弧度,而应该使用np.radians(i)
  3. 无需int(shape[0] / 2)取整,应该shape[0]//2
  4. src是img的复制,temp是src的复制,整个代码中,src明显是多余的
  5. t1仅仅是为了显式,内容和temp相同,也是多余的,第18行直接使用temp即可
  6. 这个方法太笨

为了帮助题主理解numpy,我写了几行简单的代码:

>>> import numpy as np
>>> img = np.random.randint(0,256,(3,4,3)) # 生成一个3行4列的RGB图像数组
>>> temp = img.copy() # 复制
>>> img
array([[[ 45,  95,  97],
        [198, 105, 214],
        [243, 218, 121],
        [156, 227, 212]],

       [[203, 161,  33],
        [ 88, 214,  21],
        [102, 144,  13],
        [178,  45,  17]],

       [[214, 247, 234],
        [  7,  33, 184],
        [251, 127, 151],
        [101, 162, 138]]])
>>> c = img[temp[:,:,2] > 128] # 对应temp中蓝色通道数值大于128的位置,img的蓝色通道数值组成的数组
>>> c
array([[198, 105, 214],
       [156, 227, 212],
       [214, 247, 234],
       [  7,  33, 184],
       [251, 127, 151],
       [101, 162, 138]])
>>> points = c[c < 50] # 小于50的元素
>>> points
array([ 7, 33])