FPGA
时序约束
- 如通信实验,时钟频率比较高的实验,需要进行时序约束
- 当占用芯片的逻辑资源多的时候,需要使用时序约束:因为FPGA在布线时会优先考虑面积
时钟约束用来描述设计人员对于时序的要求,包括时钟频率和输入输出延时
D触发器
arduino 机械臂
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| #include <Servo.h>
Servo myservo_1; Servo myservo_2; Servo myservo_3; int pos_1 = 0; int pos_2 = 0; int pos_3 = 0;
void setup() { myservo_1.attach(12); myservo_2.attach(11); myservo_3.attach(10); }
void loop() { myservo_1.write(0); myservo_2.write(0); myservo_3.write(0); delay(5000); for (pos_1 = 0; pos_1 <= 10; pos_1 ++) { myservo_1.write(pos_1); delay(5); } delay(1000); for (pos_2 = 0; pos_2 <= 60; pos_2 ++) { myservo_2.write(pos_2); delay(5); } delay(1000); for (pos_3 = 0; pos_3 <= 30; pos_3 ++) { myservo_3.write(pos_3); delay(5); } delay(5000);
myservo_3.write(0); delay(1000); myservo_2.write(0); delay(1000); myservo_1.write(-5);
while(1); }
|
数字电路中的逻辑值:
- 逻辑 0:表示低电平,相当于电路 GND。
- 逻辑 1:表示高电平,相当于电路 VCC。
- 逻辑 X:表示未知,高或低。
- 逻辑 Z:表示高阻态,悬空状态
Verilog 中的数字表示:
“(数字的二进制)位宽 + 进制(缩写) + 数值”来表示一个数字。
Verilog 的默认二进制位宽为32位,默认的进制为十进制。
当二进制数字位数多的时候可以使用下划线增加可读性,编译时下划线会被去掉。
标识符:
Verilog 的标识符可以用于定义模块名、端口名和信号名。
Verilog 的命名规则与 C
语言变量名的命名规则基本相同:只有一点,可以在命名中包含$符号。
标识符规则:
标识符推荐写法:
- 不建议大小写混合
- 普通内部信号全部小写
- 信号命名体现含义
- 使用下划线区分词
- 采用前后缀:比如时钟可以采用:clk_50,clk_cpu
数据类型
三种数据类型:
寄存器数据类型:实际电路物理模型
抽象数据存储单元,可以通过赋值语句改变寄存器储存的值
关键字:reg,默认初始值为 X 不确定
```verilog // reg + [位宽:31:0 指32位位宽,高位在前] +
标识符名称(寄存器名称) reg [31:0] delay_cnt; //延时计数使用的寄存器
reg key_reg; //没给位宽时默认位宽为1 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| + reg 类型的数据只能在 always 语句和 initial 语句中被赋值
+ 如果 always 中带有时钟信号即过程语句描述的是时序逻辑,则寄存器对应为触发器
+ 如果 always 中不带有时钟信号即过程语句描述的是组合逻辑,则寄存器对应为硬件连线
+
+ 线网数据类型:实际电路物理模型
+ 参数数据类型:给编译器用的
### 图像处理
OV7725摄像头
主控器控制OV7725时采用SCCB协议读写其寄存器,而它输出图像时则使用VGA或QVGA时序, 其中VGA在输出图像分辨率为480*640时采用,QVGA是Quarter VGA,其输出分辨率为240*320, 这些时序跟控制液晶屏输出图像数据时十分类似。
OV7725传感器输出图像时,一帧帧地输出,在帧内的数据一般从左到右,从上到下, 一个像素一个像素地输出(也可通过寄存器修改方向),见图 [摄像头数据输出](https://doc.embedfire.com/mcu/stm32/f103zhinanzhe/std/zh/latest/book/OV7725.html#id20) 。
![摄像头数据输出](记录/OV7725012.jpg)
例如,见图 [像素同步时序](https://doc.embedfire.com/mcu/stm32/f103zhinanzhe/std/zh/latest/book/OV7725.html#id21) 和图 [QVGA帧图像同步时序](https://doc.embedfire.com/mcu/stm32/f103zhinanzhe/std/zh/latest/book/OV7725.html#qvga) , 若我们使用D2-D9数据线,图像格式设置为RGB565,<img src="记录/image-20220326130518483.png" alt="image-20220326130518483" style="zoom:33%;" />
```verilog //RGB数据转换为YCBCR //因为FPGA处理除法是比较耗费资源的,所以我们团队采用左移右移以替代乘除法。处理整形数据可以将数据整体左移八位,再进行计算,最后在右移回去即可。
Y’ = 0.257R’ + 0.504G’ + 0.098*B’ + 16 Cb’ = -0.148R’ - 0.291G’ + 0.439*B’ + 128 Cr’ = 0.439R’ - 0.368G’ - 0.071*B’ + 128
|
进行数据输出时,D2-D9数据线在PCLK在上升沿阶段维持稳定,
并且会在1个像素同步时钟PCLK的驱动下发送1字节的数据信号,所以2个PCLK时钟可发送1个RGB565格式的像素数据。
当HREF为高电平时,像素数据依次传输,每传输完一行数据时,行同步信号HREF会输出一个电平跳变信号间隔开当前行和下一行的数据;
一帧的图像由N行数据组成,当VSYNC为低电平时,各行的像素数据依次传输,每传输完一帧图像时,VSYNC会输出一个电平跳变信号。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
rows=200 cols=300 num=2000 img=np.zeros((rows,cols,3),np.uint8) pos1=np.random.randint(200,size=(num,1)) pos2=np.random.randint(300,size=(num,1))
for i in range(num): img[pos1[i],pos2[i],[0]]=np.random.randint(0,255) img[pos1[i],pos2[i],[1]]=np.random.randint(0,255) img[pos1[i],pos2[i],[2]]=np.random.randint(0,255) 如果在OpenCV中处理图像,是BGR的顺序。
I=numpy.zeros((3,3),dtype=numpy.uint8)
I=cv2.cvtColor(I,cv2.COLOR_GRAY2BGR)
import cv2 import numpy as np
emptyImage = np.zeros(img.shape, np.uint8) cv2.imshow("EmptyImage", emptyImage)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
|
img = cv2.imread(r'C:\Users\qjy\Desktop\strawberry.jpg') cv2.imshow("Image", img)
import cv2 import numpy as np Strawberry=cv2.imread(r'C:\Users\qjy\Desktop\strawberry.jpg')
Lower = np.array([0, 0, 100]) Upper = np.array([40, 40, 255]) Binary = cv2.inRange(Strawberry, Lower, Upper) cv2.imshow("strawberry", Binary) cv2.waitKey() cv2.destroyAllWindows()
fruit = cv2.imread(r'C:\Users\qjy\Desktop\fruits.jpg') fruit = cv2.cvtColor(fruit,cv2.COLOR_BGR2YUV) Y,U,V = cv2.split(fruit) Blueberry = cv2.inRange(U,130,255) Strawberry = cv2.inRange(V,170,255) cv2.imshow("blueberry",Blueberry) cv2.imshow("strawberry",Strawberry) cv2.waitKey(0) cv2.destroyAllWindows()
import cv2 img = cv2.imread(r'C:\Users\qjy\Desktop\1.jpg', cv2.IMREAD_COLOR) ycrcb = cv2.cvtColor(img, cv2.COLOR_BGR2YCrCb) gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) (y, cr, cb) = cv2.split(ycrcb) cr1 = cv2.GaussianBlur(cr, (5, 5), 0) skin1 = cv2.threshold(cr1, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) cv2.imshow("image CR", cr1) cv2.imshow("Skin Cr+OSTU", skin1) cv2.waitKey() cv2.destroyAllWindows()
import cv2 def cr_otsu1(image): """YCrCb颜色空间的Cr分量+Otsu阈值分割 :param image: 图片路径 :return: None """ import cv2 img = cv2.imread(r'C:\Users\qjy\Desktop\3.jpg', cv2.IMREAD_COLOR) ycrcb = cv2.cvtColor(img, cv2.COLOR_BGR2YCR_CB)
(y, cr, cb) = cv2.split(ycrcb) cr1 = cv2.GaussianBlur(cr, (5, 5), 0) _, skin = cv2.threshold(cr1, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) cv2.namedWindow("image raw", cv2.WINDOW_NORMAL) cv2.imshow("image raw", img) cv2.namedWindow("image CR", cv2.WINDOW_NORMAL) cv2.imshow("image CR", cr1) cv2.namedWindow("Skin Cr+OTSU", cv2.WINDOW_NORMAL) cv2.imshow("Skin Cr+OTSU", skin)
dst = cv2.bitwise_and(img, img, mask=skin) cv2.namedWindow("seperate", cv2.WINDOW_NORMAL) cv2.imshow("seperate", dst) cv2.waitKey() cv2.destroyAllWindows()
|
灰度化
转换色彩空间:
python:
1 2 3 4 5 6 7 8 9
| import cv2 img = cv2.imread(r'C:\Users\qjy\Desktop\name_of_picture') img_ycbcr = cv2.cvtColor(img, cv2.COLOR_BGR2YCR_CB)
cv2.imshow("img_of_YCbCr", img_ycbcr) cv2.waitKey() cv2.destroyAllWindows()
|
以RGB格式的彩图为例,通常灰度化采用的方法主要有:
方法1:\(Gray=(R+G+B)/3\)
方法2:\(Gray=max(R,G,B)\)
方法3:\(Gray=0.299R+0.587G+0.114B\)(这种参数考虑到了人眼的生理特点)
所谓阈值处理,就是给定一个阈值,当像素值比指定阈值大或小时做相关的操作。==这个字念yu,不是fa==,方法签名为:cv2.threshold(src,thresh,maxval,type,dst=None)
,需要将的是OpenCV中提供的几种type:
- cv2.THRESH_BINARY:若像素值大于阈值,则置为maxval;否则置0
- cv2.THRESH_BINARY_INV:THRESH_BINARY的反转
- cv2.THRESH_TRUNC:若像素值大于阈值,则置为阈值;否则不变
- cv2.THRESH_TOZERO:小于阈值的部分置为0;其他不变
- cv2.THRESH_TOZERO_INV:THRESH_TOZERO的反转
1 2 3 4 5
| _, thresh1 = cv2.threshold(img, 127, 255, type=cv2.THRESH_BINARY) _, thresh2 = cv2.threshold(img, 127, 255, type=cv2.THRESH_BINARY_INV) _, thresh3 = cv2.threshold(img, 127, 255, type=cv2.THRESH_TRUNC) _, thresh4 = cv2.threshold(img, 127, 255, type=cv2.THRESH_TOZERO) _, thresh5 = cv2.threshold(img, 127, 255, type=cv2.THRESH_TOZERO_INV)
|
1 2 3 4 5 6 7 8 9 10 11 12
| import cv2 img = cv2.cvtColor(cv2.imread(r'C:\Users\qjy\Desktop\2.jpg'), cv2.COLOR_BGR2GRAY) threshold_value = 0 while threshold_value < 255: if threshold_value > 255: threshold_value = 255 _, thresh1 = cv2.threshold(img, threshold_value, 255, type=cv2.THRESH_BINARY) cv2.imshow("img_of_YCbCr", thresh1) cv2.waitKey(0) cv2.destroyAllWindows() threshold_value = threshold_value + 10
|
选则合适的阈值筛选不同的色块,链接下文轮廓识别
1 2 3 4 5 6 7 8 9 10 11 12
| import cv2 img = cv2.cvtColor(cv2.imread(r'C:\Users\qjy\Desktop\3.jpg'), cv2.COLOR_BGR2GRAY) threshold_value = 0 while threshold_value < 255: if threshold_value > 255: threshold_value = 255 _, thresh1 = cv2.threshold(img, threshold_value, 255, type=cv2.THRESH_BINARY) cv2.imshow("img_of_YCbCr", thresh1) cv2.waitKey(0) cv2.destroyAllWindows() threshold_value = threshold_value + 10
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| import cv2
img = cv2.cvtColor(cv2.imread(r'C:\Users\qjy\Desktop\2.jpg'), cv2.COLOR_BGR2GRAY) ret, th1 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY) th2 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 11, 2) th3 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2)
cv2.imshow("img_of_YCbCr", img) cv2.waitKey(0) cv2.destroyAllWindows()
cv2.imshow("img_of_YCbCr", th1) cv2.waitKey(0) cv2.destroyAllWindows()
cv2.imshow("img_of_YCbCr", th2) cv2.waitKey(0) cv2.destroyAllWindows()
cv2.imshow("img_of_YCbCr", th3) cv2.waitKey(0) cv2.destroyAllWindows()
|
腐蚀
1 2 3 4 5 6 7 8 9 10 11 12
| img = cv2.imread("i.png") img_noise = copy.deepcopy(img)
rows, cols = img_noise.shape[:2] for n in range(50): i = random.randint(0, rows - 1) j = random.randint(0, cols - 1) img_noise[i, j] = 255
kernel = np.ones((5, 5), dtype=np.uint8) erosion = cv2.erode(img_noise, kernel) dilation = cv2.dilate(erosion, kernel)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| im = cv2.imread('rectangle.jpg') imgray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY) ret, thresh = cv2.threshold(imgray, 127, 255, 0)
image, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
print("一共检测到%d个轮廓" % len(contours)) for i in range(len(contours)): print("第%d个轮廓:" % (i + 1)) print(contours[i])
res = cv2.drawContours(im, contours, -1, (0, 255, 0), thickness=3)
cv2.imshow("img_of_YCbCr", res) cv2.waitKey(0) cv2.destroyAllWindows()
import cv2 def find_contours_of_binary_image(): im = cv2.imread(r'C:\Users\qjy\Desktop\2.jpg') imgray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY) ret, thresh = cv2.threshold(imgray, 127, 255, 0)
image, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
print("一共检测到%d个轮廓" % len(contours)) for i in range(len(contours)): print("第%d个轮廓:" % (i + 1)) print(contours[i])
res = cv2.drawContours(im, contours, -1, (0, 255, 0), thickness=3) img_show(res)
|
轮廓检测能用代码:
1 2 3 4 5 6 7 8 9 10 11
| import cv2 import numpy as np kernel = np.ones((1, 5), np.uint8) img = cv2.imread(r'C:\Users\qjy\Desktop\1.jpg') gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) ret, binary = cv2.threshold(gray,127,255,cv2.THRESH_BINARY) binary = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel, anchor=(2, 0), iterations=5) contours, hierarchy = cv2.findContours(binary,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) cv2.drawContours(img,contours,-1,(0,0,255),3) cv2.imshow("img", img) cv2.waitKey(0)
|
能看懂的代码:
1 2 3 4 5 6 7 8 9
| import cv2 img = cv2.imread(r'C:\Users\qjy\Desktop\3.jpg') gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) ret, binary = cv2.threshold(gray,100,255,cv2.THRESH_BINARY) contours, hierarchy = cv2.findContours(binary,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) cv2.drawContours(img,contours,-1,(0,0,255),3) cv2.imshow("img", img) cv2.waitKey(0)
|
二值化
二值图也就是黑白图。将灰度图转换成黑白图的过程,就是二值化。二值化的一般算法是:
\(g={0,f≤t1,f>t}\)
其中t被称为阀值。阀值的确定方法有下面几种。
Otsu法(大津法或最大类间方差法)
来自霓虹国的大津展之为这种波谷找到了一个合适的数学表达,并于 1979
年发表论文[2]。这个二值化方法称为大津算法(Otsu’s
method)。
大津算法就是,从 1 到 255
一个个数字试,找到一个数字能够把两个波峰切开,让两个波峰内部的类内方差之和最小。类内方差之和就是,单独求这两个波峰各自的方差,乘上波峰的占比权重,然后加起来。
这个数字就能最好的把图像分开,对应的就是双峰直方图中的波谷。这个算法最多只需遍历两次直方图数组,速度飞快,至今仍被广泛应用。
图像比较复杂的时候,我们对大津算法稍加扩展也可以完成分割。对大津算法的多级推广成为多大津算法(multi
Otsu method) [3]。
*这里提到的是局部阈值的基本方法,对于实际使用中常见的其他局部阈值方法,请参阅Chow-Kaneko
自适应阈值法 [4]。
局部阈值的应用非常广泛,特别是对白纸黑字的处理非常有效。光学字符识别(OCR)和二维码扫描的算法中,很多都用了局部阈值操作。比如下面这张受光不均的二维码。
该算法是一种动态阈值分割算法。它的主要思想是按照灰度特性将图像划分为背景和目标2部分(这里我们将f≤t的部分称为背景,其他部分称为目标。),选取门限值,使得背景和目标之间的方差最大。
注:Nobuyuki
Otsu(大津展之),东京大学博士,先后在筑波大学和东京大学担任教授。
其步骤如下:
1.建立图像灰度直方图。
2.计算背景和目标的出现概率。
\(pA=∑i=0tpi,pB=∑i=t+1L−1pi=1−pA\)
其中,A和B分别表示背景部分和目标部分。
3.计算A和B两个区域的类间方差。
\(公式ωA=∑i=0tipipA,ωB=∑i=t+1L−1ipipB(公式1)\)
公式1分别计算A和B区域的平均灰度值;
公式\(ω0=pAωA+pBωB=∑i=0L−1ipi\)(公式2)
公式2计算灰度图像全局的灰度平均值;
公式\(σ2=pA(ωA−ω0)2+pB(ωB−ω0)2\)(公式3)
公式3计算A、B两个区域的类间方差。
4.针对每一个灰度值,计算类间方差。选择方差最大的灰度值,作为阀值t。
下面是几个在使用opencv作轮廓检测时需要注意的点:
- 为了更精确地提取轮廓,请使用二值图。也就是说,在使用轮廓提取函数前,请将源图片运用阈值进行
二值化(cv2.threshold())
或者采用Canny边缘检测
。
- findContours
函数会修改源图片,如果希望在轮廓检测后继续使用源图片,务必提前保存在另一个变量中。
- 在OpenCV中,轮廓检测视作从黑色背景中提取白色的物体,所以,在结果中,白色表示物体,黑色表示背景。
提取轮廓的步骤大概会是这样子:
- 读取源图片,并转化为灰度图
- 运用threshold将灰度图片二值化(也可以使用Canny边缘检测)
- 使用
findContours()函数
找到所有的轮廓
- 使用
drawContours()函数
将轮廓画出来
https://gy23333.github.io/tags/%E5%9B%BE%E5%83%8F%E5%A4%84%E7%90%86/
图像处理
形态学
膨胀
简介
膨胀是数学形态学的两种基本运算之一,腐蚀是另一种基本运算。它通常应用于二值图像,但也有适用于灰度图像的版本。该算子对二值图像的基本作用是逐渐扩大前景像素(通常为白色像素)区域的边界。因此,前景像素的区域会增大,而这些区域内的孔会变小。
它是如何运作的
这个描述的有用背景在词汇表的数学形态学部分给出。
膨胀运算符接受两段数据作为输入。首先是要放大的图像。第二个是一组坐标点(通常很小),称为结构元素(也称为内核)。正是这个构造元素决定了输入图像的精确膨胀效果。
二值图像膨胀的数学定义如下:
设X为输入二值图像对应的欧几里德坐标集,K为构造元素的坐标集。
Kx表示K的平移使其原点在x处。
那么X乘以K的膨胀就是所有点X的集合使得Kx与X的交点非空。
除了与输入图像相关联的一组坐标的导出方式之外,灰度膨胀的数学定义是相同的。此外,这些坐标是三维的,而不是二维的。
作为二元扩张的一个例子