Yolov5實現(xiàn)視頻中的指針式儀表讀數(shù) [python]
背景:根據(jù)巡航機器人拍攝的視頻,讀出其中兩個電流表和兩個電壓表的度數(shù)。
Yolov5
Yolov5的star數(shù)高達37.5k,是Yolo系列最為經(jīng)典的版本。本項目在Yolov5 v5.0的基礎(chǔ)上進行修改,來實現(xiàn)指針式儀表的讀數(shù)功能。
prepare
數(shù)據(jù)集:對機器人拍攝的視頻進行抽幀標注。
標注工具:labelImg
預(yù)訓(xùn)練權(quán)重:yolov5s.pt
環(huán)境:pip install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt
指針式儀表
整體思路
注:(train過程省略)
- 通過sftp協(xié)議從服務(wù)器下載機器人拍攝的視頻;
- 當同時檢測到四個表時才進行保存;
- 表的位置確定:通過重新排列label標簽標注表的位置(0、1、2、3);
- 找到檢測過程中最清晰的四張表:通過分別計算各個表的Laplacian,得到最清晰的表圖像;
- 在檢測完畢時,將最清晰的四張表存放在clock文件夾中;
- 利用OpenCV中的Canny算子進行圖像的邊緣檢測,識別指針和零刻度線,計算角度得出度數(shù)。
- 通過接口發(fā)送到服務(wù)器。
1. 從遠程服務(wù)器下載視頻
getVideo
:
import paramiko
import os
# 從服務(wù)器獲取視頻
HOST_IP = '10.16.42.172' # 服務(wù)器地址
PORT = 8989 // 端口
REMOTE_PATH = '/home/eini/SG/water' # 視頻在服務(wù)器的位置
REMOTE_FILENAME = 'cut4.mp4' # 文件名
LOCAL_PATH = './video' # 存放本地位置
USERNAME = 'root' # 登錄服務(wù)器用戶名
PASSWORD = '123456' # 登錄服務(wù)器密碼
def remote_scp(host_ip, remote_path, local_path, file_name, username, password):
t = paramiko.Transport((host_ip, PORT))
t.connect(username=username, password=password) # 登錄遠程服務(wù)器
sftp = paramiko.SFTPClient.from_transport(t) # sftp傳輸協(xié)議
src = remote_path + '/' + file_name
des = local_path + '/' + file_name
sftp.get(src, des)
t.close()
detect.py
:
parser.add_argument('--host_ip', default='10.16.42.172', help='')
parser.add_argument('--port', default=8989, help='')
parser.add_argument('--remote_path', default='/home/eini/SG/water', help='')
parser.add_argument('--remote_filename', default='cut1.mp4', help='')
parser.add_argument('--local_path', default='./video', help='')
parser.add_argument('--username', default='root', help='')
parser.add_argument('--password', default='123456', help='')
remote_scp(opt.host_ip, opt.remote_path, opt.local_path, opt.remote_filename, opt.username, opt.password)
2. 檢測儀表
設(shè)置變量num_clock
記錄當前檢測目標的數(shù)目,當if num_clock == 4:
時進行后續(xù)操作。
3. 重新排列l(wèi)abel標簽
此操作是為了對四個表進行標號,根據(jù)label標簽中的x坐標,從左到右將儀表標為(0、1、2、3)。detcet.py
:
# txt_path用來存放標簽信息的文件夾
sorted_label_path = txt_path + '_sorted' + '.txt'
with open(label_path, 'r') as first_file:
rows = first_file.readlines()
sorted_rows = sorted(rows, key=lambda x: float(x.split()[1]), reverse=False)
with open(sorted_label_path, 'w') as second_file:
for row in sorted_rows:
second_file.write(row)
labels文件夾
:
cut1_64.txt注:一共有5列信息,分別為 class、x、y、w、h
cut1_64_sorted.txt
4. 計算拉普拉斯算子,得到最清晰的四張表
注:這個步驟也可以通過直接計算長和寬像素值進行代替
# 事先聲明清晰度數(shù)組,用來存放比較清晰度值
sharpness_original = [0, 0, 0, 0] # 清晰度數(shù)組
# 讀取圖像
cv2.imwrite(filename_last, roi)
# 計算清晰度
img = cv2.imread(filename_last)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
sharpness = cv2.Laplacian(gray, cv2.CV_64F).var()
print("清晰度值:", sharpness, sharpness_original[n])
if sharpness > sharpness_original[n]:
sharpness_original[n] = sharpness
cv2.imwrite(clock_name, roi)
5. 檢測完畢后的四張表
將清晰度最高的四張表存放在clock文件夾中,以便于后續(xù)讀數(shù)。
6. 讀數(shù)
注:本項目中的儀表刻度盤為不均勻分布,后續(xù)需要對角度劃分進行優(yōu)化。
將儀表內(nèi)邊框的上邊線作為零刻度線的輔助線,通過計算輔助線與指針之間的夾角計算度數(shù)
電壓表讀數(shù):
def reading_V(img):
k1 = 0
k2 = 0
W = Image.open(img).width
H = Image.open(img).height
img = cv2.imread(img)
kernel = np.ones((6, 6), np.float32) / 25
gray_cut_filter2D = cv2.filter2D(img, -1, kernel)
gray_img = cv2.cvtColor(gray_cut_filter2D, cv2.COLOR_BGR2GRAY)
ret, thresh1 = cv2.threshold(gray_img, 180, 255, cv2.THRESH_BINARY)
tm = thresh1.copy()
test_main = tm[int(W / 25):int(W * 23 / 25), int(H / 25):int(H * 23 / 25)]
edges = cv2.Canny(test_main, 50, 150, apertureSize=3)
lines = cv2.HoughLines(edges, 1, np.pi / 180, 55)
lines = lines[:, 0, :]
result = edges.copy()
for rho, theta in lines:
a = np.cos(theta)
b = np.sin(theta)
x0 = a * rho
y0 = b * rho
x1 = int(x0 + result.shape[1] * (-b))
y1 = int(y0 + result.shape[1] * a)
x2 = int(x0 - result.shape[0] * (-b))
y2 = int(y0 - result.shape[0] * a)
# 零刻度線
if y1 >= H / 20 and y1 < H * 1 / 3:
k1 = line_k(x1, y1, x2, y2)
cv2.line(img, (x1, y1), (x2, y2), (0, 255, 0), 2)
# 指針
if y2 >= H / 2 and y2 <= H * 4 / 5 and x2 >= H / 8:
k2 = line_k(x1, y1, x2, y2)
cv2.line(img, (x1, y1), (x2, y2), (0, 255, 0), 2)
Cobb = int(math.fabs(np.arctan((k1 - k2) / (float(1 + k1 * k2))) * 180 / np.pi) + 0.5)
print("度數(shù)為:", int(Cobb * 250 / 90))
return int(Cobb * 250 / 90)
電流表讀數(shù):
def reading_A(img):
k1 = 0
k2 = 0
W = Image.open(img).width
H = Image.open(img).height
img = cv2.imread(img)
kernel = np.ones((6, 6), np.float32) / 25
gray_cut_filter2D = cv2.filter2D(img, -1, kernel)
gray_img = cv2.cvtColor(gray_cut_filter2D, cv2.COLOR_BGR2GRAY)
ret, thresh1 = cv2.threshold(gray_img, 180, 255, cv2.THRESH_BINARY)
tm = thresh1.copy()
test_main = tm[int(W / 25):int(W * 23 / 25), int(H / 25):int(H * 23 / 25)]
edges = cv2.Canny(test_main, 50, 150, apertureSize=3)
lines = cv2.HoughLines(edges, 1, np.pi / 180, 50)
lines = lines[:, 0, :]
result = edges.copy()
for rho, theta in lines:
a = np.cos(theta)
b = np.sin(theta)
x0 = a * rho
y0 = b * rho
x1 = int(x0 + result.shape[1] * (-b))
y1 = int(y0 + result.shape[1] * a)
x2 = int(x0 - result.shape[0] * (-b))
y2 = int(y0 - result.shape[0] * a)
# 零刻度線
if y1 >= H / 20 and y1 < H * 1 / 3:
k1 = line_k(x1, y1, x2, y2)
cv2.line(img, (x1, y1), (x2, y2), (0, 255, 0), 2)
# 指針
if y2 >= H / 2 and y2 <= H * 4 / 5 and x2 >= H / 8:
k2 = line_k(x1, y1, x2, y2)
cv2.line(img, (x1, y1), (x2, y2), (0, 255, 0), 2)
Cobb = int(math.fabs(np.arctan((k1 - k2) / (float(1 + k1 * k2))) * 180 / np.pi) + 0.5)
print("度數(shù)為:", int(Cobb * 40 / 90))
return int(Cobb * 40 / 90)
7. 保存結(jié)果
將結(jié)果保存在圖片上:
def save_clock(img, read, path):
# 設(shè)置字體,如果沒有,也可以不設(shè)置
font = ImageFont.truetype("C:\WINDOWS\FONTS\MSYHL.TTC", 50)
# 打開底版圖片
tp = Image.open(img)
# 在圖片上添加文字 1
draw = ImageDraw.Draw(tp)
draw.text((50, 50), str(read), (255, 255, 0), font=font)
# 保存
tp.save(path)
由于裁剪框的位置不同,最終結(jié)果存在一定誤差
:文章來源:http://www.zghlxwxcb.cn/news/detail-728839.html
向http接口發(fā)送結(jié)果(也可以使用postman工具測試):sendAns.py
文章來源地址http://www.zghlxwxcb.cn/news/detail-728839.html
def send(rack1_res):
conn = http.client.HTTPConnection("192.168.0.103:5000")
rack1_1 = rack1_res[0]
rack1_2 = rack1_res[1]
rack1_3 = rack1_res[2]
rack1_4 = rack1_res[3]
payload = "{\n \"username\" : \"asd123\"," \
"\n \"password\" : \"123\"," \
"\n \"rack1_1\" : \"%s\" ," \
"\n \"rack1_2\" : \"%s\"," \
"\n \"rack1_3\" : \"%s\"," \
"\n \"rack1_4\" : \"%s\"" \
"\n}" % (rack1_1, rack1_2, rack1_3, rack1_4)
headers = {
'content-type': "application/json",
'cache-control': "no-cache",
'postman-token': "a1fa6df2-a806-fdbd-6963-4342f9eb4227"
}
conn.request("POST", "/get", payload, headers)
res = conn.getresponse()
data = res.read()
到了這里,關(guān)于Yolov5實現(xiàn)視頻中的指針式儀表讀數(shù) [python]的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!