分类 编程/笔记 下的文章

老物搬运。
画面比在Linux桌面运行的流畅很多,也许是Opencv的cv2.imshow()会不断产生新窗口导致的卡顿,因为我关不掉窗口,它会一直产生新的。

Camera.py

#coding=utf-8
import cv2
class Camera():
    def __init__(self,Camera_id):
        self.video = cv2.VideoCapture(Camera_id)
        self.face_cascade = cv2.CascadeClassifier('./haarcascade_frontalface_default.xml')
    def __del__(self):
        self.video.release()
    def get_img(self):
        success,image = self.video.read()
        gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
        faces = self.face_cascade.detectMultiScale(gray, 1.3, 5)
        for (x,y,w,h) in faces:
            cv2.rectangle(image,(x,y),(x+w,y+h),(255,0,0),2)
        ret,jpeg = cv2.imencode('.jpg', image) #将image对象转为jpeg格式
        return jpeg.tobytes() #转字节

main.py

from flask import Flask,Response
from Camera import Camera
app = Flask(__name__)
@app.route('/')
def index():
    return '<h3 style="text-align:center"><img src="/video" width="60%"></h3>'

def Video_stream(camera):
    while True:
        img = camera.get_img()
        yield (b'--frame\r\n'b'Content-Type: image/jpeg\r\n\r\n' + img + b'\r\n\r\n') #生成器

@app.route('/video')
def video():
    return Response(Video_stream(Camera(0)), mimetype='multipart/x-mixed-replace; boundary=frame') #合成响应,返回一个自定义标头和图片数据

if __name__ == '__main__':
    app.run(host='0.0.0.0',port=80)

一个微小的例子,检测摄像头中出现的人脸并框出来。

环境部署

sudo apt-get install python-opencv

在Opencv的项目上下载用于检测人脸的级联:

https://github.com/opencv/opencv/blob/master/data/haarcascades/
下载 haarcascade_frontalface_default.xml
(这里面还有用于检测其他的级联文件,比如眼睛)

一个示例

人脸识别中找人脸很重要,只有检测到人脸,再进行算法比对数据库中的人脸。

#coding=utf-8
import cv2
#加载级联文件,返回一个级联分类器
face_cascade = cv2.CascadeClassifier('./haarcascade_frontalface_default.xml')
#打开摄像头
camera = cv2.VideoCapture(0)
while True:
    #读一帧
    ret, frame = camera.read()
    #转为灰度图,人脸检测需要灰度图
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    #人脸检测,返回坐标
    faces = face_cascade.detectMultiScale(gray, 1.3, 5)
    #参数:gray 灰度图像 scaleFactor 检测过程中每次迭代时图像的压缩率 minNeighbors 每个人脸矩形保留近邻数目的最小值
    for (x,y,w,h) in faces:
        #在原图像上画矩形
        cv2.rectangle(frame,(x,y),(x+w,y+h),(255,0,0),2)
    #将其显示出来
    cv2.imshow("camera", frame)
    if cv2.waitKey(1000 / 12) & 0xff == ord("q"):
        break
#关闭摄像头,销毁所有窗口
camera.release()
cv2.destroyAllWindows()

测试结果

2019-02-13-200940_1920x1080_scrot.png

性能

不得不说的是C++的性能比Python快至少一半,但我觉得由于现在计算机性能并不像以前那么弱,所以C++与Python没有什么区别,当然,像嵌入式设备这种性能不强的还是用C++比较好。

去网上查了一圈资料后,还是觉的os好用。

权限表

常量名权限
os.O_RDONLY只读
os.O_WRONLY只写
os.O_RDWR读写
os.O_APPEND追加
os.O_CREATE不存在时创建文件
os.O_TRUNC打开时截断文件

写文件

package main
import ("fmt"
        "os")
func checkerr(err error){
        if err != nil{
                fmt.Println(err)
                os.Exit(1)
        }
}
func main(){
        f,err := os.OpenFile("test.txt",os.O_RDWR|os.O_APPEND|os.O_CREATE,0644) //注意文件权限
        checkerr(err)
        defer f.Close()
        data := []byte("-1s -1s -1s\n") //转字节
        bytesw,err := f.Write(data) //写操作
        checkerr(err)
        fmt.Println("bytes:",bytesw)
}

读文件

package main
import ("fmt"
        "os")
func checkerr(err error){
        if err != nil{
                fmt.Println(err)
                os.Exit(1)
        }
}
func main(){
        f,err := os.Open("test.txt") //使用这种方式为只读
        //f,err := os.OpenFile("test.txt",os.O_RDONLY,0644)
        checkerr(err)
        defer f.Close()
        b := make([]byte,6) //读取6个字节,如果要读取全部,要用ioutil.ReadAll(fileObj)
        br,err := f.Read(b)
        checkerr(err)
        fmt.Println("bytes:",br)
        fmt.Println("data:",b)
        fmt.Println("str:",string(b))
}


从网上查到有Go中有两个包可以读写文件,一个是os包,另一个是ioutil包。
我主要用的是ioutil包,用起来比较简单,当然os也是有他的优势的。

读取文件

package main
import ("fmt"
        "reflect"
        "io/ioutil")
func main(){
        data,err := ioutil.ReadFile("test.txt")
        if err != nil{
                fmt.Println(err)
        }
        fmt.Println("Type:",reflect.TypeOf(data))
        fmt.Println("Data",string(data))
}
//output:
Type: []uint8
Data China Mogic Association
Founded in 1926, near the Huangpu river.
//可以看到,读出的是uint8类型的切片

写文件

这会覆盖文件!

package main
import "io/ioutil"
func checkrerr(err error){
        if err != nil{
                panic(err)
        }
}
func main(){
        data := []byte("+1s +1s +1s") //转字节
        err := ioutil.WriteFile("test.txt",data,0644)//文件掩码:-rw-r--r--
        checkrerr(err)
}

ERROR

概念

除了panicrecover之外,Go中还有一个内置的错误接口类型,任何类型只要实现Error() string方法,都可以传递error接口 类型变量,Go语言处理错误的方式是将error作为函数最后一个返回值,在调用函数时,通过检查其返回的error值是否为nil来进 行错误处理。

type error interface{ //接口类型
    Error() string
}

Go语言提供了两个函数返回实现了error接口的具体类型实例,一般的错误可以使这两个函数进行封装,当然,遇到复杂的错误也可以 自定义错误类型,只要实现error接口即可:

func New(text string) error{
    return &errorString{text}
}

最佳实践

  1. 在多个返回值的函数中,error一般作为最后一个返回值
  2. 如果一个函数返回error类型的变量,则先判断是否为nil
  3. defer语句应该放到err判断后面,不然可能产生panic
  4. 在错误逐层向上传递的过程中,错误信息应该不断的丰富与完善,而不是简单的抛出下层调用的错误,便于分析错误

33_error_0.PNG

其他要说的

Golang全面深入系列之 Error:https://studygolang.com/articles/12625
Go 语言如何实现error的优雅处理:https://blog.csdn.net/baogang409/article/details/72604333

阶段性总结

学了有一段时间了,也写了一些Go,感觉执行速度挺快的,毕竟不像Python这种解释性语言。部分语法与Python也很相像,从Python转到Go也不觉的陌生,学Go主要是写网络方面的程序,看网上的评价说Go对于网络编程性能还不错,要不然会有ngrok这种内网穿透的东东出 现。最后,路还长着,一次只学一点,细嚼慢咽的学。

概念

panicrecover是Go的两个内置函数,这两个内置函数用于处理Go运行时的错误,panic用于主动抛出错误,recover用来捕获panic抛出的错误。

3441746ab2b9d09.jpg

  • 引发panic有两种情况,一是程序主动调用,二是程序产生运行时错误,由运行时检测并退出。
  • 发生panic后,程序会从调用panic的函数位置或发生panic的地方立即返回,逐层向上执行函数的defer语句,然后逐层打印 函数调用堆栈,直到被recover捕获或运行到最外层函数。
  • panic不但可以在函数正常流程中抛出,在defer逻辑里也可以再次调用panic或抛出panicdefer里面的panic能够被后 续执行的defer捕获。
  • recover用来捕获panic,阻止panic继续向上传递。recover()defer一起使用,但是defer只有在后面的函数体内直接 被掉用才能捕获panic来终止异常,否则返回nil,异常继续向外传递。

例子1

//以下捕获失败
defer recover()
defer fmt.Prinntln(recover)
defer func(){
    func(){
        recover() //无效,嵌套两层
    }()
}()

//以下捕获有效
defer func(){
    recover()
}()

func except(){
    recover()
}
func test(){
    defer except()
    panic("runtime error")
}

例子2

多个panic只会捕捉最后一个:

package main
import "fmt"
func main(){
    defer func(){
        if err := recover() ; err != nil {
            fmt.Println(err)
        }
    }()
    defer func(){
        panic("three")
    }()
    defer func(){
        panic("two")
    }()
    panic("one")
}

使用场景

一般情况下有两种情况用到:

  1. 程序遇到无法执行下去的错误时,抛出错误,主动结束运行
  2. 在调试程序时,通过panic来打印堆栈,方便定位错误

其他要说的

go panic与recover分析及错误处理:https://blog.csdn.net/a41888313/article/details/79691728

概念

闭包是由函数及其相关引用环境组合而成的实体,一般提供通过在匿名函数中引用外部函数的局部变量或包全局变量构成。
即:闭包 = 函数 + 引用环境
闭包对闭包外的环境引入是直接引用,编译器检测到闭包,会将闭包引用的外部变量分配到栈上。
如果函数返回的闭包引用了该函数的局部变量(参数或函数内部变量):

  1. 多次调用该函数,返回的多个闭包所引用的外部变量是多个副本,原因是每次调用函数都会为局部变量分配内存
  2. 用一个闭包函数多次,如果该闭包修改了其引用的外部变量,则每一次调用该闭包对该外部变量都有影响,因为闭包函数共享外部引用
package main
func test(a int) func(i int) int{
    return func(i int) int{
        println(&a,a)
        a = a + i
        return a
    }
}
func main(){
    //f,g引用的外部的闭包环境包括本次函数调用的形参a的值1
    f := test(1)
    g := test(1)
    //多次调用的f,g引用的a为同一个a
    println(f(1))
    println(f(1))
    println(g(1))
    println(g(1))
}
//output
0x103100c0 1
2
0x103100c0 2
3
0x103100c4 1
2
0x103100c4 2
3

建议

如果一个函数调用返回的闭包引用修改了全局变量,则每次调用都会影响全局变量。
如果函数返回的闭包引用的是全局变量 a ,则多次调用该函数的返回的多个闭包引用的都是同一个 a 。同理,调用一个闭包多次引用一个 a 。此时如果闭包中修改了 a 值的逻辑,则每次闭包调用都会影响全局变量 a 的值。使用闭包是为了减少全局变量,所以闭包引用 全局变量不是好的编程方式。

 package main
 var a = 0
 func test() func(i int) int{
     return func(i int) int{
         println(&a,a)
         a = a + i
         return a
     }
 }
 func main(){
     f := test()
     g := test()
     //此时f,g引用的闭包环境中的a是同一个
     println(f(1))
     println(g(1))
     println(g(1))
     println(g(1))
     println(f(1))
 }
//output
 0xa9e9c 0
 1
 0xa9e9c 1
 2
 0xa9e9c 2
 3
 0xa9e9c 3
 4
 0xa9e9c 4
 5

价值

闭包最初的目的是减少全局变量,在函数调用的过程中隐式的传递共享变量,有其有用的一面;但是这种隐瞒的共享变量的方式带来的 坏处是不够直接,不够清晰,除非是非常有价值的地方,一般不会使用闭包。
对象是附有行为的数据,而闭包是附有数据的行为,类在定义是就已经显式的集中定义了行为,但是闭包中的数据没有显式的集中 声明的地方,这种数据和行为耦合的模型不是一种推荐的编程模型,闭包只是锦上添花的东西,不是必不可少的。
——《Go 语言核心编程》

其他要说的

什么是闭包?闭包的优缺点?:https://www.cnblogs.com/cxying93/p/6103375.html

函数签名

函数签名

函数类型又叫函数签名,一个函数的类型就是函数定义首行去掉函数名,参数名和{,可以使用fmt.Printf("%T",func)

package main
import "fmt"
func test(a int) int{
    a++
    return a
}
func main(){
    fmt.Printf("%T",test)
}
//output:
func(int) int

特点

  1. 可以用type来定义函数类型,函数类型可以作为函数的参数或返回值。
  2. 函数可以赋值与变量,可以使函数绑定一个变量,通过变量来调用
func test(a int) int{
    a++
    return a
}
func main(){
    t := test
    println(t(1))
}

匿名函数

顾名思义,没有函数名的函数,可直接赋值给变量,可作为实参,也可作为返回值,亦可直接调用。

package main
var moha = func(hath,hath2 int) int{
    return hath + hath2
}
func refunc() func(int)int{
    return func(a int) int{
        a++
        return a
    }
}
func infunc(f func(int) int) int{
    return f(90,2)
}
func main(){
    println(moha(1,2)) //直接调用
    println(refunc()(1)) //调用返回的函数
    println(infunc(moha)) //传入函数
}

延迟调用

defer

Go中提供defer来提供延迟调用,可以注册多个延迟调用,这些延迟调用遵循FILO(先进后出),顺序在函数返回前被执行。defer常用于保证一些资源最终一定能得到回收和释放,比如文件读写。

package main
func main(){
    defer func(){
        println("three")
    }()
    defer func(){
        println("two")
    }()
println("one")
}

特点

  1. FILO(先进后出)
  2. defer后必须是函数或方法的调用,不能为语句,否则报错:expression in defer must be function call
  3. defer函数的实参在注册时通过值拷贝传递进去,后续的操作语句不会对defer的函数造成影响
package main
func main(){
    a := 1
    defer func(a int){
        println(a)
    }(a) //值拷贝
    a++
    println(a)
}
  1. defer只有注册后才能执行,即在return前有效,在return后因为没有被注册而不会执行
  2. 主动退出(os.Exit(int))defer不会被执行
  3. defer最好不要调用有名函数,否则会产生一些奇奇怪怪的错误
  4. defer一般放在错误检查语句后
  5. 最好不要放在循环里

其他要说的

Go 中 defer 的 5 个坑:https://studygolang.com/articles/12061?fr=sidebar