最新要闻

广告

手机

英国房地产因利率上升陷入困境 房价正以2011年来最快速度下跌

英国房地产因利率上升陷入困境 房价正以2011年来最快速度下跌

宁夏评选出上半年10名“宁夏好人” 95后消防员因敬业奉献入选

宁夏评选出上半年10名“宁夏好人” 95后消防员因敬业奉献入选

家电

opencv-python图像处理模块(一)

来源:博客园

1 图像颜色空间转换


【资料图】

opencv提供了用于颜色空间转换的函数,用来适应在不同需求中的图像使用要求。 dst = cvtColor(img, mode) mode表示颜色空间转换方式(转换到RGB空间:COLOR_BGR2RGB;转换成灰度图片:COLOR_BGR2GRAY;转换到HSV空间:COLOR_BGR2HSV;转换到YUV空间:COLOR_BGR2YUV)

颜色空间转换代码如下:

import cv2import numpy as np  #图片颜色空间转换cv2.namedWindow("color",cv2.WINDOW_NORMAL)  #定义窗口cv2.resizeWindow("color",(640,480))img = cv2.imread("./cat.jpg")def callback(value):   #回调函数    passcolor_space = [cv2.COLOR_BGR2BGRA, cv2.COLOR_BGR2RGB,  cv2.COLOR_BGR2GRAY, cv2.COLOR_BGR2HSV,cv2.COLOR_BGR2YUV]cv2.createTrackbar("trackbar","color",0,4,callback)  #创建进度条while True:    index = cv2.getTrackbarPos("trackbar","color")  #获取进度条数值    img2 = cv2.cvtColor(img,color_space[index])  #图片颜色空间转换        cv2.imshow("color",img2)    key = cv2.waitKey(1)    if key == ord("q"):        breakcv2.destroyAllWindows()

除了最常用的RGB空间,HSV空间在有些时候也用的比较多,这里稍微介绍一下HSV空间,HSV即色相(Hue)、饱和度(Saturation)、亮度(Value)。HSV的颜色空间更符合人类对颜色的理解,人眼很难根据图像的rgb值推测出图像到底是什么颜色,但是可以根据hsv的值大致推测出图像的颜色,或者看到某一种颜色可以推测出对应的hsv值。

色调(H): 用角度度量,取值范围为0°~360°,从红色开始按逆时针方向计算,红色为0°,绿色为120°,蓝色为240°。它们的补色是:黄色为60°,青色为180°,品红为300°;在opencv中 取值范围是0-180。

饱和度(S)表示颜色接近光谱色的程度。一种颜色,可以看成是某种光谱色与白色混合的结果。其中光谱色所占的比例愈大,颜色接近光谱色的程度就愈高,颜色的饱和度也就愈高。饱和度高,颜色则深而艳。光谱色的白光成分为0,饱和度达到最高。通常取值范围为0%~100%,值越大,颜色越饱和。在opencv中 取值范围是0-255。

亮度(V)表示颜色明亮的程度,对于光源色,明度值与发光体的光亮度有关;对于物体色,此值和物体的透射比或反射比有关。通常取值范围为0%(黑)到100%(白)。在opencv中 取值范围是0-255,事实上可以超过这个范围。

HSV柱状图如下:

常见颜色的大致hsv范围:

比如下图,狗的颜色大致是橙色,我希望通过颜色把狗单独提取出来。但是这个橙色不是确定的某一种颜色,而是一个范围,可以使用 OpenCV 的inRange函数来检测和提取图像中存在的颜色。具体是使用该函数来创建颜色掩码,mask = inRange(img,LowerBlue, UpperBlue) img表示待处理图像,LowerBlue表示低阈值范围,UpperBlue表示高阈值范围。inRange函数将颜色的值设置为 1,如果颜色存在于给定的颜色范围内,则设置为白色,如果颜色不存在于指定的颜色范围内,则设置为 0。

import cv2import numpy as np  #根据颜色提取roi区域img = cv2.imread("./cat_dog.jpg")print(img.shape)mask = cv2.inRange(img,(70,90,160),(105,150,250))  #用inRange创建颜色掩膜dog = cv2.bitwise_and(img,img,mask=mask)  #用掩膜与原图像与运算获取roi区域cv2.imshow("images",img)cv2.imshow("dog",dog)cv2.waitKey(0)cv2.destroyAllWindows()

直接用rgb空间来获取颜色掩膜非常困难,经过多次尝试也没法获得比较满意的效果,如果转换到hsv空间后效果会好很多,如下:

import cv2import numpy as np  #转换到HSV根据颜色提取roi区域img = cv2.imread("./cat_dog.jpg")print(img.shape)HSV = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)H, S, V = cv2.split(HSV)LowerBlue = np.array([0, 100, 150])UpperBlue = np.array([20, 160, 240])mask = cv2.inRange(HSV,LowerBlue,UpperBlue)#mask = cv2.inRange(img,(70,90,160),(105,150,225))print(HSV)dog = cv2.bitwise_and(img,img,mask=mask)cv2.imshow("images",img)cv2.imshow("dog",dog)cv2.waitKey(0)cv2.destroyAllWindows()

总体来说,效果还是很不错的,比rgb提取的效果好很多。

2 基本图形绘制

1)绘制标记

opencv中提供了用于绘制标记的函数drawMarker,可以使用该函数在图像上标记一个点。 dst = drawMarker(img, position, color, markerType=None, thickness=None) position表示标记绘制的位置,需要传入整型;color表示标记的绘制颜色;markerType表示标记绘制类型,thickness表示标记线的粗细。

import cv2import numpy as np  #绘制标记 drawMarker(图像,位置,颜色,标志类型,标记线粗细)img = cv2.imread("./cat.jpg")draw_marker = cv2.drawMarker(img,(250,250),(255,0,0),cv2.MARKER_CROSS,thickness=3)  #绘制十字架draw_marker = cv2.drawMarker(img,(50,50),(0,255,0),cv2.MARKER_STAR,thickness=3)  #绘制星型draw_marker = cv2.drawMarker(img,(400,400),(0,0,255),cv2.MARKER_DIAMOND,thickness=3) #绘制菱形draw_marker = cv2.drawMarker(img,(50,400),(255,255,0),cv2.MARKER_SQUARE,thickness=3) #绘制长方形draw_marker = cv2.drawMarker(img,(400,50),(255,0,255),cv2.MARKER_TILTED_CROSS,thickness=3) #绘制x型print(img.shape)cv2.imshow("img",img)cv2.waitKey(0)cv2.destroyAllWindows()

2)绘制直线和矩形

opencv提供的直线绘制函数line,dst = line(ing,pt1,pt2,color,thickness=None,lineType=None) pt1表示绘制直线的第一个点,pt2表示第二个点;color表示绘制的直线颜色;thickness表示绘制直线的粗细;lineType表示绘制的线型。绘制矩形的函数为rectangle,dst = rectangle(img, pt1,pt2, color, thickness=None, lineType=None) pt1表示绘制矩形的顶点,pt2表示与pt1相对的顶点。

绘制的图形可以用窗口显示,也可以显示在图像上。

import cv2  #绘制直线,矩形#img = cv2.imread("./cat.jpg")img = np.zeros((414,500,3),np.uint8)draw_line1 = cv2.line(img,(50,50),(400,400),(0,0,255),thickness=3) #绘制直线draw_line2 = cv2.line(img,(50,400),(400,50),(0,0,255),thickness=3)draw_rectangle = cv2.rectangle(img,(50,50),(400,400),(255,0,0),thickness=3) #绘制矩形cv2.imshow("img",draw_line1)   #此时的img和draw——line1,line2,rectangle是一样的了,相当于浅拷贝,共用一个内存cv2.waitKey(0)cv2.destroyAllWindows()

3)绘制圆和椭圆

opencv提供了用于绘制圆的函数时circle,dst = circle(img, center, radius, color, thickness=None, lineType=None) center表示圆心坐标,radius表示圆心半径。(注:thickness=-1的时候,绘制实心图形)

此外,opencv还提供了绘制椭圆的函数ellipse,dst = ellipse(img, center, axes, angle, startAngle, endAngle, color, thickness=None, lineType=None) center表示椭圆的圆心坐标,axes表示主轴尺寸的一半,angle表示椭圆旋转的角度,startAngle表示椭圆圆弧的起始角度,endAngle表示椭圆的终止角度(即可以只绘制椭圆的一部分)。

import cv2  #绘制圆,椭圆img = cv2.imread("./cat.jpg")img_copy =img.copy()draw_circle = cv2.circle(img,(300,200),150,(0,0,255),thickness=3)#绘制圆draw_marker = cv2.drawMarker(img,(300,200),(0,0,255),cv2.MARKER_CROSS,thickness=3) #标记圆心draw_ellipse = cv2.ellipse(img,(100,200),(100,50),90,0,360,(0,255,0),thickness=3) #绘制椭圆draw_marker = cv2.drawMarker(img,(100,200),(0,255,0),cv2.MARKER_CROSS,thickness=3) #标记椭圆圆心draw_ellipse = cv2.ellipse(img_copy,(100,200),(100,50),90,0,180,(0,255,0),thickness=3) #绘制半个椭圆draw_ellipse = cv2.ellipse(img_copy,(300,200),(100,100),180,0,180,(0,0,255),thickness=3) #绘制半个圆cv2.imshow("img",img)cv2.imshow("img_copy",img_copy)cv2.waitKey(0)cv2.destroyAllWindows()

4)绘制多边形

除了绘制上面的常见图形,也可以自定义坐标,然后根据坐标来绘制多边形,opencv绘制多边形的函数为polylines,dst = polylines(img, pts, isClosed, color[, thickness[, lineType]])

pts表示存放点集坐标的二维数组,isClosed表示是否把绘制的多条线段首尾相连,thickness 线宽的值必须大于0。还可以绘制填充多边形,fillPoly(img, pts, color[, lineType])

import cv2    #绘制多边形import numpy as npimg = np.zeros([480,640,3],np.uint8)#多边形的点集,必须是int32位pts = np.array([(50,50),(50,100),(100,200),(200,400),(400,100)],np.int32)#cv2.polylines(img,[pts],True,[0,0,255],thickness=3) #绘制多边形cv2.fillPoly(img,[pts],[255,0,0])    #绘制填充多边形cv2.imshow("lines",img)cv2.waitKey(0)cv2.destroyAllWindows()

5)绘制文字

在图像处理中 ,经常要对图像增加一些说明性文字,opencv提供了用于绘制文字的函数putText, dst = putText(img,text,org,fontFace,fontscale,color,thickness=None) text是待绘制的文字,org表示文字在图像中绘制区域的左下角位置,fontFace表示字体,fontScale表示对字体的缩放比例,color表示颜色,thickness表示绘制文字的粗细。text也可以由用户手动输入。

import cv2   #绘制文字,opencv不能绘制中文img = cv2.imread("./cat.jpg")#text = "this is a cat"text = input("enter your input:")  #从键盘自己输入文字draw_text = cv2.putText(img,text,(200,50),cv2.FONT_ITALIC,1,(0,0,255),thickness=2)cv2.imshow("img",draw_text)cv2.waitKey(0)cv2.destroyAllWindows() 

此外,opencv没法直接绘制中文字符,可以用pillow包绘制中文,需要用的中文字体在windows下的fonts里面拷贝出来(我拷贝了微软雅黑字体),拷贝到当前路径会自动命名msyhbd.ttc。

具体代码如下:

import cv2  #opencv没法绘制中文文本,可以用pillow包绘制中文,需要用的中文字体在windows下的fonts里面拷贝出来import numpy as npfrom PIL import ImageFont,ImageDraw,Image  img = cv2.imread("./cat.jpg")font = ImageFont.truetype("./msyhbd.ttc",20)  #导入字体文件,参数是字体大小img_pil = Image.fromarray(img)  #创建一个pillow的图片print(img_pil)draw = ImageDraw.Draw(img_pil)   #利用draw绘制中文text = "这是一只猫"draw.text((250,10),text,font=font,fill=(0,0,255))img = np.array(img_pil) #把图片重新变回ndarray格式才能显示  cv2.imshow("img",img)cv2.waitKey(0)cv2.destroyAllWindows()

3 利用鼠标和键盘控制绘制图形

首先介绍一下opencv的鼠标操作,鼠标事件回调函数为setMouseCallback,定义为:setMouseCallback(windowname, onMouse) windowname为窗口名,onMouse为鼠标事件回调函数。鼠标事件类型有MouseEventType定义,鼠标事件常用的主要有:EVENT_LBUTTONDBLCLK(左键双击),EVENT_RBUTTONDBLCLK(右键双击),EVENT_LBUTTONDOWN(按下左键),EVENT_RBUTTONDOWN(按下右键),EVENT_LBUTTONUP(左键释放),EVENT_RBUTTONUP(右键释放),EVENT_MOUSEMOVE(移动鼠标)。

1)通过鼠标操作绘制图形

本例通过不同的鼠标操作进行图形绘制,双击可以绘制圆,鼠标左键按下拖动绘制红色线,鼠标右键按下拖动绘制蓝色线。

import cv2           import numpy as npstart_point=(0,0) #鼠标开始坐标和结束坐标(其实这里只需要定义一个鼠标坐标点就行)end_point=(0,0)  #必须把这些参数定义为全局变量,如果在函数内部的话,每次调用函数这些参数都会被重置lb_down,lb_up,rb_down,rb_up = False,False,False,False #鼠标按键按下/抬起的标志,bool型def mouse_event(event,x,y,flags,param):    #鼠标回调函数 (event是鼠标事件,x,y是鼠标指针当前所指的坐标)    global start_point,end_point,lb_down,lb_up,rb_down,rb_up #如果全局变量是int或者str,那么如果想要在函数中对函数变量进行修改,则需要                                                           #先在函数内,声明其为global,再进行修改,如果是list或者dict则可以直接修改    if event == cv2.EVENT_LBUTTONDBLCLK:        cv2.circle(img,(x,y),100,(0,0,255),thickness=3)  #双击左键绘制圆        elif event == cv2.EVENT_LBUTTONDOWN:  #左键按下,更新鼠标坐标,启动按下标志        start_point = (x,y)        end_point = start_point        lb_down = True        elif event == cv2.EVENT_RBUTTONDOWN:  #右键按下,更新鼠标坐标,启动按下标志        start_point = (x,y)        end_point = start_point        rb_down = True        elif event == cv2.EVENT_MOUSEMOVE:  #鼠标移动,绘制线        if lb_down:   #如果左键按下,绘制红色线            cv2.line(img,start_point,(x,y),(0,0,255),thickness=3)        if rb_down:   #如果右键按下,绘制蓝色线            cv2.line(img,start_point,(x,y),(255,0,0),thickness=3)        start_point = (x,y)   #只要鼠标移动,就更新鼠标的坐标            elif event == cv2.EVENT_LBUTTONUP: #左键释放        lb_up = True        lb_down = False        cv2.line(img,start_point,(x,y),(0,0,255),thickness=3)  #鼠标点击后直接释放鼠标的时候也会绘制一个点            elif event == cv2.EVENT_RBUTTONUP:  #右键释放        rb_up = True        rb_down = False        cv2.line(img,start_point,(x,y),(255,0,0),thickness=3)  #鼠标点击后直接释放鼠标的时候也会绘制一个点        #img = np.zeros((512,512,3),np.uint8)  #创建一个黑色图像img = cv2.imread("./cat.jpg")cv2.namedWindow("image")   #新建窗口cv2.setMouseCallback("image",mouse_event)  #设置鼠标回调while True:    cv2.imshow("image",img)    if cv2.waitKey(1)==ord("q"): #waitKey参数不能写0,写0就需要键盘输入才会继续        breakcv2.destroyAllWindows()        

2)通过鼠标键盘配合绘制图形

本来通过鼠标键盘绘制图形:按下l,拖动鼠标,绘制直线;按下r,拖动鼠标,绘制矩形;按下c,拖动鼠标,绘制圆。首先,绘制图形,需要记录起始位置start_point,按下鼠标左键的时候记录起始位置,鼠标左键释放的时候作为终点,绘制图形。还需要一个参数flag_key用来记录绘制那种图形的标志。

import cv2import numpy as npstart_point = (0,0)  #鼠标起始位置flag_key = 0  #选择绘制图形标志def mouse_callback(event,x,y,flags,userdata):  #鼠标回调函数    global start_point,flag_key  #声明全局变量        if event == cv2.EVENT_LBUTTONDOWN:  #按下鼠标左键,记录起始位置        start_point = (x,y)            elif event == cv2.EVENT_LBUTTONUP: #松开鼠标左键,绘制图形        if flag_key == 0:            cv2.line(img,start_point,(x,y),[0,0,255],3)  #绘制直线                    elif flag_key ==1:            cv2.rectangle(img,start_point,(x,y),[0,255,0],3) #绘制矩形                    elif flag_key ==2:            a = x - start_point[0]            b = y - start_point[1]            radius = int((a**2 + b**2)**0.5)    #计算圆心            cv2.circle(img,start_point,radius,[255,0,0],3,) #绘制圆img = np.zeros((800,800,3),np.uint8) cv2.namedWindow("image")  cv2.setMouseCallback("image",mouse_callback)  #设置鼠标回调while True:    cv2.imshow("image",img)    key =cv2.waitKey(1)  #键盘按键判断,更新绘制图形标志    if key == ord("l"):        flag_key=0    elif key == ord("r"):        flag_key=1    elif key == ord("c"):        flag_key=2    elif key == ord("q"):        breakcv2.destroyAllWindows()    

3)在图像上面显示某一点的坐标和对应的像素值

有时需要根据图像的像素值进行某些操作,图片上可以设置双击左键获取坐标和对应的rgb值,双击右键获取坐标和对应的hsv值。主要涉及的是鼠标操作setMouseCallback 和 绘制文字操作putText。

# 直接根据鼠标双击获取图像当前位置的像素值import cv2import numpy as npimg = cv2.imread("./cat.jpg")img_hsv = cv2.cvtColor(img,cv2.COLOR_BGR2HSV) #转换到hsv空间#print(img.shape)def mouse_event(event,x,y,flags,param):    if event == cv2.EVENT_LBUTTONDBLCLK:   #双击左键显示图像的坐标和对应的rgb值        print("img pixel value at(", x, ",", y, "):",img[y, x])  #坐标(x,y)对应的像素值应该是img[y,x]        text = "(" + str(x) + "," + str(y) + ")" + str(img[y,x])         cv2.putText(img,text,(x,y),cv2.FONT_HERSHEY_SIMPLEX,0.5,(255,0,0),1)  #绘制文字            if event == cv2.EVENT_RBUTTONDBLCLK:   #双击右键显示图像的坐标和对应的hsv值        print("img_hsv pixel value at(", x, ",", y, "):",img_hsv[y, x])  #坐标(x,y)对应的像素值应该是img_hsv[y,x]        text = "(" + str(x) + "," + str(y) + ")" + str(img_hsv[y,x])        cv2.putText(img,text,(x,y),cv2.FONT_HERSHEY_SIMPLEX,0.5,(0,0,255),1)  #绘制文字cv2.namedWindow("image",cv2.WINDOW_NORMAL)  #定义窗口#cv2.resizeWindow("image",(800,800)) cv2.setMouseCallback("image",mouse_event)  #鼠标回调while True:    cv2.imshow("image",img)    if cv2.waitKey(1)==ord("q"):        breakcv2.destroyAllWindows()

4 给图像加水印logo

用矩形或者圆等组合自己绘制一个合适的logo,然后把logo添加到图像上适当位置,比如如下logo,加入cat图片的右上角。

把logo水印插入到指定的图片中,比较简单的想法是:从图片适当位置取出与logo大小一致的图片切片,然后与logo进行操作,这个操作可以把logo水印添加到图片切片中且不影响到切片背景,然后直接把融合后的切片放到之前的位置即可。这个操作还涉及到一个比较重要的东西,mask的作用。mask是掩膜,可以用来遮盖非感兴趣区,突出感兴趣区,使得图像处理只专注于ROI部分。

mask是一个二值图像,与对应尺寸的图片进行与操作可以获取感兴趣的区域,比如下列提取猫头的简单操作:

import cv2import numpy as npimg = cv2.imread("./cat.jpg")print(img.shape)mask = np.zeros(img.shape[:2],np.uint8) #和原图像尺寸一样大的掩膜mask[50:180,30:190] = 255 #猫头的大概位置roi = cv2.bitwise_and(img,img,mask=mask)  #mask与对应图像进行与操作提取roi区域cv2.imshow("img",img)cv2.imshow("mask",mask)cv2.imshow("roi",roi)cv2.waitKey(0)cv2.destroyAllWindows()

因此,根据刚才的思路,提取logo的掩膜,然后与图片切片进行掩膜操作提取logo外围黑色区域所需的背景,然后把所得图片和logo进行相加即可获得融合logo的图片。

import cv2   #给图像加上水印logoimport numpy as npimg = cv2.imread("./cat.jpg")w,h,c = img.shapeimg = cv2.resize(img,(int(h*1.5),int(w*1.5)),interpolation=cv2.INTER_AREA)  #图片放大1.5倍logo = np.zeros((200,200,3),np.uint8)  #创建logo图片窗口logo[20:120,20:120]=[0,0,255]  #logo上绘制矩形logo[80:180,80:180]=[255,0,0]cv2.circle(logo,(100,100),50,(0,255,0),-1) #绘制实心圆mask = np.zeros((200,200),np.uint8)  #构造一个掩模图像,将该掩模图像作为按位与函数的掩模参数,实现保留图像的指定部分mask[20:120,20:120]=255              #掩码是二维矩阵,当使用掩模参数时,操作只会在掩模值为非空的像素点上执行,并将其他像素点的值置为0mask[80:180,80:180]=255for i in range(200):                 #把logo绿色通道的圆对应的mask也拷贝过去    for j in range(200):        if logo[i][j][1] ==255:            mask[i][j]=255        else:            passm = cv2.bitwise_not(mask)  #因为要插入logo时要保证logo附近的背景不变,因此这里用mask把logo附近的背景取出来,所以需要mask取反temp = img[20:220,500:700]img2 = cv2.bitwise_and(temp,temp,mask=m) #提取跟logo窗口一样大的图片,logo标志像素为0,背景与原背景相同dst = cv2.add(img2,logo) img[20:220,500:700]=dst  #把最终的融合背景的logo放到图片对应的位置cv2.imshow("logo",logo)  cv2.imshow("mask",mask)  cv2.imshow("img2",img2)cv2.imshow("img",img)cv2.waitKey(0)cv2.destroyAllWindows()

logo,logo对应的掩膜,具有掩膜的背景图片切片和融合logo后的切片图片如下:

最终的添加logo后的图片如下:

关键词: