树莓派编译OpenCV4

确保当前目录为 /home/opencv4/,确保树莓派可用所有内存(物理内存和虚拟内存)大于2GB

先看看有没有git,没有的话就

sudo apt install git

克隆项目:

git clone https://github.com/opencv/opencv.git
git clone https://github.com/opencv/opencv_contrib.git

速度比较慢的话可以挂个梯子或者使用码云

//基本操作软件源更新,软件包升级(注意版本)
sudo apt update
sudo apt upgrade

然后是安装各种包(缺少的话make的时候会提示)

sudo apt install libjpeg8-dev
sudo apt install libtiff5-dev
sudo apt install libjasper-dev
//sudo apt install libpng12-dev 这个和GTK2.0冲突
sudo apt install libavcodec-dev
sudo apt install libavformat-dev
sudo apt install libswscale-dev
sudo apt install libv4l-dev
sudo apt install libgtk2.0-dev

进入OpenCV项目目录

cd opencv

然后创建build文件夹,并进入

mkdir build
cd build

CMake

cmake \
   -D CMAKE_BUILD_TYPE=RELEASE \
   -D CMAKE_INSTALL_PREFIX=/usr/local \
   -D INSTALL_PYTHON_EXAMPLES=ON \
   -D OPENCV_EXTRA_MODULES_PATH=/home/opencv4/opencv_contrib/modules \
   -D BUILD_EXAMPLES=ON ..

开始编译(我这边是4核4GB内存的Pi4,所以直接开启4核编译,温馨提示:注意散热)

make -j4

编译完毕之后直接

sudo make install

Arduino UNO给Arduino Nano刷程序 (Arduino As ISP)

废话不多说,材料:Arduino UNO、Arduino Nano、杜邦线、Arduino UNO的USB连接线、Arduino IDE。

(一)先按照上图将Arduino UNO和Arduino Nano用杜邦线连起来。

(二)给UNO刷上 ArduinoISP 程序 :
“菜单 >> File(文件) >> Examples(示例) >> 11.ArduinoISP >> ArduinoISP”
这个时候你的UNO就变成了一个编程器
如下图所示:

(三)配置选项
选择:
“菜单 >> Tools(工具)>> Board(板): Arduino Nano”
“菜单 >> Tools(工具)>> Processor(处理器): ATmega328p (Old Bootloader)”
“菜单 >> Tools(工具)>> Programmer(编程器): Arduino As ISP”
然后打开你要刷的程序
如下图所示:

若是空片,则需要点击:
“菜单 >> Sketch(项目) >> Burn Bootloader(烧录引导程序)”
如果不是空片就跳过这一步

(四)编译+通过Arduino As ISP编程器上传程序
点击:
“菜单 >> Sketch(项目) >> Upload Using Programmer(使用编程器上传)”
或者可以使用快捷键Ctrl+Shift+U
也可以按住Shift点上传按钮

完工

一些零零散散的东西

1.关于Arduino As ISP刷写进去之后芯片运行速度很慢
这个一看就是熔丝位没设置好,烧录一下引导程序即可,这个会自动设置熔丝位。

2.关于ArduinoISP程序的SPI Clock
SPI Clock必须要比芯片时钟频率慢至少4倍以上,例如Atmega328p可以使用16MHz的外部晶振,因此SPI Clock可以设置为16000000/6,但是写入空片的时候由于芯片使用的是8MHz的内部晶振而不是16MHz的外部晶振,SPI Clock需要低于8000000/4,否则会无法读取芯片签名。

2.关于Arduino As ISP刷写速度
找到:
#define BAUDRATE 19200
改成
#define BAUDRATE 115200
刷进Uno,同时,在你Arduino的安装目录里找到hardware\arduino\avr里的programmer.txt,把arduinoasisp.name=Arduino as ISP 下的 arduinoasisp.speed和arduinoasisp.program.speed后面的19200改成115200,重启Arduino IDE即可

遇到的树莓派蓝牙Serial的坑(RFCOMM)

第一次接触树莓派蓝牙,卡了好久,现在解决了,我就记录下来这些坑。

这边用NodeJS来做蓝牙的Server端,使用的是经典蓝牙,Python的话应该也是差不多的,毕竟就是读写串口。

首先是日常的安装依赖库

sudo apt install pi-bluetooth bluez bluez-firmware blueman

修改蓝牙的服务

sudo nano /etc/systemd/system/dbus-org.bluez.service

修改ExecStart,增加ExecStartPost,然后保存

ExecStart=/usr/lib/bluetooth/bluetoothd -C
ExecStartPost=/usr/bin/sdptool add SP

重启树莓派

NodeJS这边需要准备 serialport 库

npm install serialport

树莓派的蓝牙打开之后,手机的蓝牙串口工具可能没办法连接,这个时候需要监听蓝牙的hci0设备,如果没有其他串口,默认在/dev/rfcomm0,/dev/rfcomm0只有在有设备连接的时候才会自动创建,所以如果没有设备连接的时候打开串口会报错,需要做检测。

sudo rfcomm watch hci0

接下来就是代码了:


var SerialPort = require("serialport")
var bthServer = new SerialPort("/dev/rfcomm0",{
  baudRate:9600, //默认比特率为9600
  autoOpen:false, //是否自动开启,如果是的话下方的open函数就不需要了
}

bthServer.open(function(err){
  console.log("Is opened:",bthServer.isOpen)
  console.log("Error:",err) //如果有错误的话就开不了
}

bthServer.on("data",function(data){
  console.log(data) //输出蓝牙接收到的数据
  bthServer.write("Received") //往回发数据
}

8086汇编指令Wiki

数据传送指令

1.通用数据传送指令

指令含义中文描述
MOV Move 传送
PUSH Push onto the stack 进栈
POP Pop from the stack 出栈
XCHG Exchange 交换

2.累加器专用传送指令

指令含义中文描述
INInput 输入
OUTOutput 输出
XLATTranslate 换码

3.有效地址送寄存器指令

指令含义中文描述
LEALoad effective address有效地址送寄存器
LDSLoad DS with Pointer指针送寄存器和DS
LESLoad ES with Pointer指针送寄存器和ES

4.标志寄存器传送指令

指令含义中文描述
LAHFLoad AH with flags标志寄存器送进AH
SAHFStore AH into flagsAH 送进标志寄存器
PUSHFPush the flags标志进栈
POPFPop the flags标志出栈

算术指令

1.加法指令

指令含义中文描述
ADDAdd加法
ADCAdd with carry带进位加法
INCIncrement自加1

2.减法指令

指令含义中文描述
SUBSubtract减法
SBBSubtract with borrow带借位减法
DECDecrement自减1
NEGNegate求补
CMPCompare比较

3.乘法指令

指令含义中文描述
MULUnsigned Multiple无符号数乘法
IMULSigned Multiple带符号数乘法

4.除法指令

指令含义中文描述
DIVUnsigned divide无符号数除法
IDIVSigned divide带符号数除法
CBWConvert byte to word字节转换为字
CWDContert word to double word字转换为双字

逻辑指令

1.逻辑运算指令

指令含义中文描述
ANDAnd逻辑与
OROr逻辑或
NOTNot逻辑非
XORExclusive Or异或
TESTTest测试

2.移位指令

指令含义中文描述
SHLShift logical left逻辑左移
SALShift arithmetic left算术左移
SHRShift logical right逻辑右移
SARShift arithmetic right算术右移
ROLRotate left循环左移
RORRotate right循环右移
RCLRotate left through carry带进位循环左移
RCRRotate right through carry带进位循环右移

串处理指令

指令含义中文描述
MOVSMove string传送串
STOSStore string保存串
LODSLoad string加载串
CMPSCompare string比较串

控制转移指令

1.无条件转移指令

指令含义中文描述
JMPJump 无条件转移

2.条件转移指令

指令含义(转移条件)中文描述
JZ / JEJump if zero,
or equal
结果为零或相等
JNZ / JNEJump if not zero,
or not equal
结果不为零或不相等
JSJump if sign结果为负
JNSJump if not sign结果为正
JOJump if overflow溢出
JNOJump if not overflow不溢出
JP / JPEJump if parity,
or parity even
奇偶位为1
JNP / JPOJump if not parity,
or parity odd
奇偶位为0
JB / JNAE / JCJump if below,
or not above or equal,
or carry
低于,
或者不高于等于,
或进位位为1
JNB / JAE / JNCJump if not below,
or above or equal,
or not carry
不低于,
或者高于等于,
或进位位为0
JBE / JNAJump if below or equal,
or not above
低于等于,
或不高于
JNBE / JAJump if not below or equal,
or above
不低于等于,
或者高于
JL / LNGEJump if less,
or not greater or equal
带符号数小于,
或者不大于等于
JNL / JGEJump if not less,
or greater or equal
带符号数不小于,
或者大于等于
JLE / JNGJump if less or equal,
or not greater
带符号数小于等于,
或者不大于
JNLE / JGJump if not less or equal,
or greater
带符号数不小于等于,
或者大于
JCXZJump if CX register is zero CX寄存器的内容为零

3.循环指令

指令含义(循环条件)中文描述
LOOPLoop无条件
LOOPZ / LOOPELoop if zero当为零或相等时
LOOPNZ / LOOPNELoop if not zero当不为零或不相等时

4.子程序

指令含义中文描述
CALLCall调用指令
RETReturn返回指令

5.中断

指令含义中文描述
INTInterupt中断
INTOInterupt if overflow若溢出则中断
IRETReturn from interupt从中断返回指令

处理机控制指令

1.标志处理指令

指令含义中文描述
CLCClear carry进位位置0指令CF=0
CMCComplement carry进位位求反指令CF=Not CF
STCSet carry进位位置1指令CF=1
CLDClear direction方向标志置0指令DF=0
STDSet direction方向标志置1指令DF=1
CLIClear interrupt中断标志置0指令IF=0
STISet interrupt中断标志置1指令IF=1

2.其他处理机控制指令

指令含义中文描述
NOPNo Opreation无操作
HLTHalt停机
WAITWait等待
ESCEscape换码
LOCKLock封锁

Lua 5.1代码效率优化

用Lua的时候可能需要涉及到效率的情况,毕竟Lua代码量大了之后速度肯定会有所降低,这边就是经历了各种效率测试积累的经验并且记录下来。

辟谣:
1、lua中乘以0.5和除以2的效率是一样的,因此不需要纠结乘除法,反而除以2还少一个字节。

提示:
1、Lua访问函数的开销非常大,即尽量减少函数的调用
2、访问全局变量比局部变量慢,因为访问全局变量需要查全局表

数学算法加速:

--math.abs实现,效率提高至少5倍
y = x < 0 and -x or x
--math.floor实现,效率提高至少5倍
y = x - x % 1
--math.ceil实现,效率提高至少2倍
y = x - x % 1 + ( ( x % 1 == 0) and 0 or 1 )
--math.max实现,效率提高至少8倍
y = a <= b and b or a
--math.min实现,效率提高至少8倍
y = a >= b and b or a
--math.deg实现,效率提高至少8倍
y = x*180/3.1415926535898
--math.rad实现,效率提高至少8倍
y = x/180*3.1415926535898

Lua在Linux上使用Sox进行批量处理MP3音频

首先需要Sox和MP3格式支持

apt install sox
apt install libsox-fmt-mp3

然后是lua程序

主程序 main.lua

local filelist = io.popen('find input -name "*.mp3"')	--扫描MP3
local fileContent = filelist:read("*all")	--读取文字段
filelist:close()	--关闭popen
os.execute("mkdir output")
os.execute("rm -rf SOXLUA")	--移除锁储存的目录
os.execute("mkdir SOXLUA")	--创建锁储存的目录

function split( str,reps )	--字符串分段
    local resultStrList = {}
    string.gsub(str,'[^'..reps..']+',function ( w )
        table.insert(resultStrList,w)
    end)
    return resultStrList
end

function getPart(str)	--大段路径转换成表
	local str,lin = string.gsub(str,"\r","")
	return split(str,"\n")
end
local ret = getPart(fileContent)
local totalLine = #ret	--总行数
local cnt = 0	--当前正在执行

function getPool()	--获取处理池,一旦有空池,立刻返回,否则阻塞主程序
	while(true) do
		os.execute("sleep 0.01")	--防止while造成高cpu使用率
		for i=1,12 do	--一共12个处理程序,循环检测
			local f = io.open("SOXLUA/SOXLUA"..i..".lock")	--检查锁文件是否存在
			if not f then return i end	--不存在则返回id,表示可用
			if f:read(2) == "ad" then	--如果存在,则读取这个锁是否已经被释放
				f:close()	--关闭检查的文件
				return i	--返回id,表示可用
			else
				f:close()	--关闭检查的文件
			end
		end
	end
end

for i,line in ipairs(ret) do	--主循环
	cnt = cnt+1	--每次循环+1表示当前处理进程
	local dir = split(line,"/")	--把路径写成表
	table.remove(dir,1)	--从表中移除"input"
	local fileName = dir[#dir]	--记录文件名
	table.remove(dir,#dir)	--从表中移除文件名
	local path = ""	--最终路径
	for i=1,#dir do	--层层创建路径,并且写入path变量
		path = path.."/"..dir[i]
		os.execute("mkdir 'output"..path.."'")	--创建目录
	end
	print("["..cnt.."/"..totalLine.."]"..line.." --> output"..path.."/"..fileName)	--打印进程数据
	local soxCommand = 'sox '..line..' -C 128 output/'..path..'/'..fileName	--转码的指令,!此处根据实际需求来修改代码!,传递给sox.lua的参数
	local poolID = getPool()	--获取空闲处理程序池
    io.popen("lua sox.lua '"..soxCommand.."' '".."SOXLUA/SOXLUA"..getPool()..".lock".."'")	--不加:close则为异步启动处理程序
end

子程序 sox.lua

local arg = {...}	--获取参数
local file = io.open(arg[2],"w")	--打开锁文件
file:write("a")	--写入a表示该锁已被使用
file:flush()	--立刻保存
io.popen(arg[1]):close()	--运行Sox指令
file:write("d")	--完成后,写入d表示该锁已经被释放
file:close()	--关闭锁

然后要把转码的文件丢到同目录下的input目录(没有自己创建),然后输入lua main.lua运行程序

处理完毕之后,输出在同目录下的output目录,并且保存目录结构

Lua批量重命名+序列化

一个一个手打太麻烦,win的cmd又不是很懂的。

编译完官网上下完lua配置完系统环境变量,直接使用如下代码

local path = "C:\\Users\\thisdp\\Pictures\\Camera Roll\\"
local filelist = io.popen('dir "'..path..'"'):read("*all")
local start_pos = 0
local count = 0
while true do
	_,end_pos,line = filelist:find("([^ \n\r]+.jpg)", start_pos)
	if not end_pos then break end
	count = count + 1
	local source,target = path..line,path.."Left_"..count..".jpg"
	print(source,target,os.rename(source,target))
	start_pos = end_pos + 1
end

使用DLL的Lua解释器编译

直接编译Lua会让所有代码都写入Lua.exe,,由于没有Lua解释器的动态链接库,所以只能使用完整的Lua静态库编译Module,这样编译的Module在使用require加载的时候会出现Multiple VM Detected的报错。

将Lua编译成DLL,会自动生成对应的.lib静态库。再将lua.c单独拿出来编译,导入编译DLL生成的静态库,生成一个依赖于动态链接库的Lua Console。用此静态库编译的Module将会依赖于Lua 解释器的动态链接库,完毕。