访问和修改像素值
先来理解一下,图像与一般的矩阵或张量有何不同(不考虑图像的格式,元数据等信息)。首先,一张图像有自己的属性,宽,高,通道数。其中宽和高是我们肉眼可见的属性,而通道数则是图像能呈现色彩的属性。我们都知道,光学三原色是红色,绿色和蓝色,这三种颜色的混合可以形成任意的颜色。常见的图像的像素通道也是对应的R,G,B三个通道,在OpenCV中,每个通道的取值为0~255,。(注:还有RGBA,YCrCb,HSV等其他图像通道表示)。即,一般彩色图像读进内存之后是一个h * w * c的矩阵,其中h为图像高(相当于矩阵的行),w为图像宽(相当于矩阵列),c为通道数。
比如说一张青色的图片,如图所示
青色是绿色和蓝色的混合,所以该图像的所有像素值都为R := 0; G := 255; B := 255
我们可以通过图像的行和列坐标来访问像素值,常见的RGB图像由红绿蓝三个通道组成,维度为三。
这里随意导入一张图片,名称为2.png,可以看到成功输出了图像的各方面信息。
#NoEnv ;不检查空变量是否为环境变量(建议所有新脚本使用)。
#Include opencv_ahk_lib.ahk
SendMode Input
hOpencv := DllCall("LoadLibrary", "str", "opencv_world455.dll", "ptr")
hOpencvCom := DllCall("LoadLibrary", "str", "autoit_opencv_com455.dll", "ptr")
DllCall("autoit_opencv_com455.dll\DllInstall", "int", 1, "wstr", A_IsAdmin = 0 ? "user" : "", "cdecl")
cv := ComObjCreate("OpenCV.cv") ;创建 COM 对象。
img := cv.imread("2.png") ;img := cv.imread("2.png")等于img := cv.imread("2.png", 1) 这里的1就是默认参数
;图像宽度 矩阵的行数
rows := img.rows()
;图像高度 矩阵的列数
cols := img.cols()
;矩阵元素拥有的通道数,例如常见的彩色图像,每一个像素由RGB三部分组成,则channels = 3
channels := img.channels()
;访问中心点像素值
B := img.Vec3b_at(rows/2, cols/2)[0]
G := img.Vec3b_at(rows/2, cols/2)[1]
R := img.Vec3b_at(rows/2, cols/2)[2]
MsgBox, 宽度:%rows%`n高度:%cols%`n颜色通道数:%channels%`nB通道颜色值:%B%`nG通道颜色值:%G%`nR通道颜色值:%R%
有了这些信息之后,下面可以根据坐标修改像素值。(方法来自Autohotkey中文社区群友zzZ…)
;修改像素值
loop, % rows{
index_rows := A_Index
loop, % cols{
index_cols := A_Index
img.Vec3b_set_at(index_rows - 1, index_cols - 1, ComArrayMake([255, 0, 0])) ;这里的三个参数对应B,G,R三个颜色通道
}
}
cv.imshow("Image", img)
cv.waitKey()
cv.destroyAllWindows()
这里不难看出,我们修改了B通道的值为255后,整个图片变为蓝色。
访问图像属性
图像属性包括行数,列数和通道数,图像数据类型,像素数等。
;uchar型的指针。Mat类分为了两个部分:矩阵头和指向矩阵数据部分的指针,data就是指向矩阵数据的指针。
data := img.data()
;矩阵的维度,例如5*6矩阵是二维矩阵,则dims=2,三维矩阵dims=3.
dims := img.dims()
;矩阵的大小,size(cols,rows),如果矩阵的维数大于2,则是size(-1,-1)
size_cols := img.size()[0]
size_rows := img.size()[1]
;下面的几个属性是和Mat中元素的数据类型相关的。
type := img.type()
;表示了矩阵中元素的类型以及矩阵的通道个数,它是一系列的预定义的常量,其命名规则为CV_(位数)+(数据类型)+(通道数)。具体的有以下值:
; CV_8UC1 CV_8UC2 CV_8UC3 CV_8UC4
; CV_8SC1 CV_8SC2 CV_8SC3 CV_8SC4
; CV_16UC1 CV_16UC2 CV_16UC3 CV_16UC4
; CV_16SC1 CV_16SC2 CV_16SC3 CV_16SC4
; CV_32SC1 CV_32SC2 CV_32SC3 CV_32SC4
; CV_32FC1 CV_32FC2 CV_32FC3 CV_32FC4
; CV_64FC1 CV_64FC2 CV_64FC3 CV_64FC4
; 这里U(unsigned integer)表示的是无符号整数,S(signed integer)是有符号整数,F(float)是浮点数。
; 例如:CV_16UC2,表示的是元素类型是一个16位的无符号整数,通道为2.
; C1,C2,C3,C4则表示通道是1,2,3,4
; type一般是在创建Mat对象时设定,如果要取得Mat的元素类型,则无需使用type,使用下面的depth
depth := img.depth()
; 矩阵中元素的一个通道的数据类型,这个值和type是相关的。例如 type为 CV_16SC2,一个2通道的16位的有符号整数。那么,depth则是CV_16S。depth也是一系列的预定义值,
; 将type的预定义值去掉通道信息就是depth值:
; CV_8U CV_8S CV_16U CV_16S CV_32S CV_32F CV_64F
elemSize := img.elemSize()
;矩阵一个元素占用的字节数,例如:type是CV_16SC3,那么elemSize = 3 * 16 / 8 = 6 bytes
elemSize1 := img.elemSize1()
;矩阵元素一个通道占用的字节数,例如:type是CV_16CS3,那么elemSize1 = 16 / 8 = 2 bytes = elemSize / channels
MsgBox, dims: %dims%`nsize_cols: %size_cols%`nsize_rows: %size_rows%`ntype: %type%`ndepth: %depth%`nelemSize: %elemSize%`nelemSize: %elemSize1%
拆分和合并图像通道
将 BGR 图像分割为单个通道,再将这些单独的通道连接到 BGR 图像。
#NoEnv
#Include opencv_ahk_lib.ahk
SendMode Input
hOpencv := DllCall("LoadLibrary", "str", "opencv_world455.dll", "ptr")
hOpencvCom := DllCall("LoadLibrary", "str", "autoit_opencv_com455.dll", "ptr")
DllCall("autoit_opencv_com455.dll\DllInstall", "int", 1, "wstr", A_IsAdmin = 0 ? "user" : "", "cdecl")
cv := ComObjCreate("OpenCV.cv")
new_img := ComObjCreate("OpenCV.cv.Mat")
img := cv.imread("2.png")
;cv.namedWindow("Image")
cv.imshow("Image", img)
;拆分图像通道
mv := ComObjCreate("OpenCV.VectorOfMat")
cv.split(img, mv)
cv.imshow("Image_B", mv.at(0))
cv.imshow("Image_G", mv.at(1))
cv.imshow("Image_R", mv.at(2))
;拆分后直接合并图像通道到新矩阵
cv.merge(mv, new_img)
cv.imshow("new_img", new_img)
;把B通道全部置为0,然后合并
newaddchannel := mv.at(0).clone()
newaddchannel.setTo(ComArrayMake([0]))
new_channels := ComObjCreate("OpenCV.VectorOfMat")
new_channels.push_back(newaddchannel)
new_channels.push_back(mv.at(1))
new_channels.push_back(mv.at(2))
mergedImage := ComObjCreate("OpenCV.cv.Mat")
mergedImage := cv.merge(new_channels, mergedImage)
cv.imshow("hebin", mergedImage)
cv.waitkey()
cv.destroyAllWindows()
制作图像边界
#NoEnv
#Include opencv_ahk_lib.ahk
SendMode Input
SetWorkingDir %A_ScriptDir%
hOpencv := DllCall("LoadLibrary", "str", "opencv_world455.dll", "ptr")
hOpencvCom := DllCall("LoadLibrary", "str", "autoit_opencv_com455.dll", "ptr")
DllCall("autoit_opencv_com455.dll\DllInstall", "int", 1, "wstr", A_IsAdmin = 0 ? "user" : "", "cdecl")
cv := ComObjCreate("OpenCV.cv")
img := cv.imread("2.png")
border_default := ComObjCreate("OpenCV.cv.Mat")
border_default := cv.copyMakeBorder(img, 50, 50, 50, 50, 0, ComArrayMake([0, 0, 0]))
cv.imshow("11", border_default)
CV.waitkey()
可以看到,这里成功画出了厚度为50像素的框。
相关链接:https://www.cnblogs.com/wangguchangqing/p/4016179.html
天黑版opencv_ahk.dll使用(改变了调用方式,优化速度…)
相关文件:https://wwz.lanzouw.com/iAkK803eaaud
cv2.ahk和log.ahk来自社区群友zzZ…
可以用文件中的天黑版的v2h版ahk运行。
示例:图像的基本操作
#Dllload lib
#DllLoad opencv_ahk.dll
#include <cv2>
#include <log>
SetWorkingDir A_ScriptDir
;初始化opencv模块
cv := ObjFromPtr(DllCall('opencv_ahk.dll\opencv_init', 'ptr', DllCall(A_AhkPath '\ahkGetApi', 'ptr'), 'cdecl ptr'))
cv.namedWindow("image")
cv.moveWindow("image", 100, 100)
img := cv.imread("image/test.png")
;宽,高,颜色通道数,中心点像素值
rows := img.rows
cols := img.cols
channels := img.channels
BGR := img[rows/2, cols/2]
log.info(rows, cols, channels, BGR)
; data := img.data().ptr
dims := img.dims
; size := img.size
type_ := img.type
depth := img.depth
elemSize := img.elemSize
elemSize1 := img.elemSize1
log.info(dims, type_, depth, elemSize, elemSize1)
;改变中心点为白色
img[rows/2, cols/2] := [255, 255, 255]
cv.imshow("image", img)
;改变所有像素点为白色
img.setTo(cv.MAT(1, 3, cv2.CV_8UC3, [255, 255, 255]))
cv.imshow("image1", img)
;拆分合并图像通道
img := cv.imread("image/lena.png")
vm := cv.Vector_Mat()
cv.split(img, vm)
cv.imshow("image_B", vm[0])
cv.imshow("image_G", vm[1])
cv.imshow("image_R", vm[2])
;拆分后直接合并图像通道到新矩阵
new_img := cv.MAT()
cv.merge(vm, new_img)
cv.imshow("new_img", new_img)
;B通道像素全部改为0,然后合并
vm[0].setTo(cv.MAT(1, 1, cv2.CV_8UC3, [0, 0, 0]))
cv.imshow("image_newB", vm[0])
new_img := cv.MAT()
cv.merge(vm, new_img)
cv.imshow("new_img1", new_img)
;制作图像边界
border_default := cv.MAT()
cv.copyMakeBorder(img, border_default, 50, 50, 50, 50, cv2.CV_BORDER_CONSTANT, [0, 0, 0])
cv.imshow("new_img2", border_default)
cv.waitKey()
cv.destroyAllWindows()
有错误请联系我改正!
本系列所有贡献者(AutoHotKey中文社区群友)不分先后:天黑请闭眼,zzZ…,演好自己,僵尸,城西,Tebayaki。
谢谢分享