外部程序¶
MeterVision 程序在与其他能够处理*机器视觉识别结果*的程序结合使用时可达到最佳效果。 例如,这类程序可以是 METLAB 或用户自编的软件。
为此,程序提供了两种方式将*识别结果*发送到外部程序
(output 中的文件与 socket 服务器;在 识别结果 中启用相应模式)。
同时,也可以通过外部程序以两种方式控制 MeterVision 的某些功能:
通过 程序目录 下的文本文件
input/command.txt。通过 socket(需在 设置 中启用「通过 socket 传输结果」)——使用相同的命令语法; 每条命令单独一行;服务器回复
OK/ERROR: ...``(对 ``get则返回结果)。
可在文件或 socket 中一次发送多条命令:每条命令单独占一行。 命令按从第一行到最后一行的顺序执行。
可使用的命令如下:
关闭 MeterVision 程序
exit
保存 截图
保存到默认文件夹(output/screenshots/ 在 程序目录 中),文件名使用默认名称(创建日期和时间)
screenshot
保存到默认文件夹,但使用自定义文件名
screenshot my_name.png
保存到指定文件夹,并使用自定义文件名
screenshot path/to/my/dir/my_name.png
打开区域设置文件
open path/to/my/dir/settings.mv
固定背景(仅限指针方法)
fix_background n
其中 n 为目标区域编号
取消背景固定(仅限指针方法)
unfix_background n
其中 n 为目标区域编号
固定中心(仅限指针方法)
fix_center n
其中 n 为目标区域编号
取消中心固定(仅限指针方法)
unfix_center n
其中 n 为目标区域编号
忘记区域统计(仅限指针方法)
forget_stats n
其中 n 为目标区域编号
忘记背景(仅限指针方法)
forget_background n
其中 n 为目标区域编号
切换到其他刻度(仅限指针方法)
scale n m
其中 n 为目标区域编号,m 为要切换到的刻度编号
切换结果显示模式(仅限指针方法)
结果为刻度读数
result_mode n scale
结果为指针倾斜角(度)
result_mode n angle
其中 n 为目标区域编号
input/command.txt 文件示例¶
screenshot my_name.png
unfix_background 1
fix_center 1
scale 1 3
result_mode 1 angle
通过 socket 发送命令与获取结果¶
启用 socket 模式后(见 识别结果),外部程序连接到 input/socket_config.txt 中的 host/port,
并发送以换行结尾的文本行。
命令与应答以换行符分隔。一次连接可发送多条命令;服务器对每条命令回复一行
(get 的回复形如 {1: '24.10', 2: 'AC'},其他命令回复 OK 或 ERROR: ...)。
get或results— 获取当前识别结果(一次请求对应一次回复; 可在同一连接上重复调用任意次数)。上表中的任意命令 — 执行操作;回复
OK或ERROR: ...。
命令序列示例:
get
fix_center 1
scale 1 2
获取结果示例(examples/socket_results_example.py)。
可在同一连接上多次调用 get_results():
import socket
import time
class MeterVisionClient:
def __init__(self, host='localhost', port=65432):
self.host = host
self.port = port
self.sock = None
self._buffer = ''
def connect(self):
"""连接到 MeterVision 服务器。"""
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.connect((self.host, self.port))
self._buffer = ''
print(f"已连接到服务器 {self.host}:{self.port}")
def get_results(self):
"""
请求当前结果。可在同一连接上多次调用。
返回形如 "{1: '24.10', 2: 'AC'}" 的字符串。
"""
if self.sock is None:
raise RuntimeError('请先调用 connect()')
self.sock.sendall(b'get\n')
while '\n' not in self._buffer:
chunk = self.sock.recv(4096)
if not chunk:
raise ConnectionError('服务器已关闭连接')
self._buffer += chunk.decode('utf-8')
line, self._buffer = self._buffer.split('\n', 1)
return line
def close(self):
"""关闭连接。"""
if self.sock is not None:
self.sock.close()
self.sock = None
print('连接已关闭')
if __name__ == "__main__":
client = MeterVisionClient()
try:
client.connect()
for i in range(5):
results = client.get_results()
print(f"[{i + 1}] 收到:", results)
time.sleep(1)
except ConnectionRefusedError as e:
print(e)
except KeyboardInterrupt:
print('连接已中断')
finally:
client.close()
发送命令示例(examples/socket_commands_example.py):
import socket
def send_commands(commands, host='localhost', port=65432):
"""
发送命令列表并打印服务器回复。
Args:
commands: 不含结尾 \\n 的命令字符串列表
host, port: MeterVision 服务器地址
"""
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
client_socket.connect((host, port))
print(f"已连接到服务器 {host}:{port}")
except ConnectionRefusedError as e:
print(e)
return
try:
payload = ''.join(cmd.strip() + '\n' for cmd in commands if cmd.strip())
client_socket.sendall(payload.encode('utf-8'))
buffer = ''
replies_expected = sum(1 for cmd in commands if cmd.strip())
replies_got = 0
while replies_got < replies_expected:
data = client_socket.recv(4096)
if not data:
break
buffer += data.decode('utf-8')
while '\n' in buffer:
line, buffer = buffer.split('\n', 1)
print("回复:", line)
replies_got += 1
if replies_got >= replies_expected:
break
except KeyboardInterrupt:
print('连接已中断')
finally:
client_socket.close()
if __name__ == "__main__":
# 示例:请求结果并为指针区域执行命令
send_commands([
'get',
'fix_center 1',
'result_mode 1 scale',
])
备注
上述示例也位于程序安装目录下的 examples/socket_results_example.py 与
examples/socket_commands_example.py。