zaizizaizai

a dream of a child


  • 首页

  • 分类

  • 关于

  • 归档

  • 标签

  • 搜索

binder监测连接状态

发表于 2020-05-06 | 分类于 android | 阅读次数

Binder监测连接状态,以及断开后的重连

一、监测连接状态

1. binder对象死亡通知机制

1.1. 注册死亡通知

client和server通过binder通信,且binder提供一个方法,即传入死亡代理binder.linkToDeath(IBinder.DeathRecipient) ,当client于server断开的时候,client收到DeathRecipient的binderDied()方法回调 ,从而知道连接断开,以下为binder源码:

  • BBinder::linkToDeath ()

    1
    2
    3
    4
    5
    6
    status_t BBinder::linkToDeath(
    const sp<DeathRecipient>& /*recipient*/, void* /*cookie*/,
    uint32_t /*flags*/)
    {
    return INVALID_OPERATION; // 返回无效操作
    }

    server端的linkToDeath只返回无效操作

  • BpBinder::linkToDeath ()

    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
    status_t BpBinder::linkToDeath(
    const sp<DeathRecipient>& recipient, void* cookie, uint32_t flags)
    {
    Obituary ob;
    ob.recipient = recipient;
    ob.cookie = cookie; //标识死亡接收者
    ob.flags = flags; //

    LOG_ALWAYS_FATAL_IF(recipient == NULL,
    "linkToDeath(): recipient must be non-NULL");

    {
    AutoMutex _l(mLock);

    if (!mObitsSent) { //mObitsSent用来表示Binder驱动程序是否已经向它发送过死亡通知,如果是,直接返回DEAD_OBJECT
    if (!mObituaries) { //第一次注册
    mObituaries = new Vector<Obituary>; //死亡通知对象列表
    if (!mObituaries) {
    return NO_MEMORY;
    }
    ALOGV("Requesting death notification: %p handle %d\n", this, mHandle);
    getWeakRefs()->incWeak(this); //增加了弱引用计数
    IPCThreadState* self = IPCThreadState::self();
    self->requestDeathNotification(mHandle, this); // 记录对象
    self->flushCommands(); //促使当前线程马上通过IO控制命令BINDER_WRITE_READ进入到Binder驱动程序中,以便可以执行注册死亡接受通知的操作
    }
    ssize_t res = mObituaries->add(ob); //非第一个注册
    return res >= (ssize_t)NO_ERROR ? (status_t)NO_ERROR : res;
    }
    }

    return DEAD_OBJECT;
    }

    Binder代理对象内部的成员变量mObitsSent用来表示Binder驱动程序是否已经向它发送过死亡通知

    • 若是,表示Binder本地对象已经死亡,返回DEAD_OBJECT
    • 若否,
      • 若第一次注册,记录Binder代理对象的地址,并立马调用Binder Driver执行注册死亡通知
      • 非第一次注册,直接加入死亡通知对象列表mObituaries
  • binderDied()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class IBinder : public virtual RefBase
    {
    public:

    class DeathRecipient : public virtual RefBase
    {
    public:
    virtual void binderDied(const wp<IBinder>& who) = 0;//重写父类DeathRecipient的成员函数binderDied
    };

    ......
    };

    自定义的死亡通知接受者必须要重写父类DeathRecipient的成员函数binderDied。当Binder驱动程序通知一个Binder代理对象它所引用的Binder本地对象已经死亡时,就会调用它所指定的死亡通知接受者的成员函数binderDied

  • 简述流程

    • Binder代理对象将一个死亡接收通知注册到Binder驱动程序中
    • 当Binder驱动程序监控到它所引用的Binder本地对象死亡时,Binder驱动程序就会向Binder代理对象发送一个死亡通知

    由BBinder::linkToDeath ()和BpBinder::linkToDeath ()代码可见

    • server进程死亡时,运行在其里的Binder对象就死亡,通过比较Binder代理存在,本地对象为空可知server端死亡,会通知到client代理对象,pmcs模块中即为BnPmcsProxy对象
    • client进程死亡时,没有通知任何对象。这时候Binder驱动程序就应该向那些引用了它的Binder代理对象发送死亡接受通知,以便它们可以知道自己引用了一个无效的Binder本地对象
  • 一句话总结

    client可以知道连接断开,server不知道什么时候断开,只有通过连接它的client来获得连接状态

1.2. 发送死亡接收通知

​ 暂略

2. client检测连接状态

由 binder对象死亡通知机制 可知,client端(PmcsProxy )可通过binder.linkToDeath(IBinder.DeathRecipient)获得连接状态,即server挂掉,通知Binder代理对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
bool PmcsProxy::attachServer()
{
const sp<IPmcsService>& service(getPmcsService());

if(service.get() != NULL)
{
if (mDeathNotifier == NULL) {
mDeathNotifier = new DeathNotifier<PmcsProxy>(this);
}
service->asBinder()->linkToDeath(mDeathNotifier); // 注册死亡通知
return true;
}
return false;
}

3. server检测连接状态

由 binder对象死亡通知机制 可知,server自身无法检测连接状态,只能通过与server端连接的client端来获取连接状态,即通过client端(PmcsProxy )的binder.linkToDeath(IBinder.DeathRecipient),那么server端只要获取到client对象就可以检测连接状态

  • server如何获取和区分client?两种方法
    • server端给每个连接上的client端一个标识token
      • client类增加一个token属性
      • 当创建一个client时,server给其分配一个token标识
    • 利用Binder的static方法,Binder.getCallingPid()来拿到client端的pid

二、连接断开后的重连

由上文 监测连接状态 可知,在重写binderDied函数时,再次连接就好了

  • client检测到server断开,client重启server
  • server检测到client断开,server重创建一个client

binder-LinkToDeath

发表于 2020-05-06 | 分类于 android | 阅读次数

Pmcs类图理解

1. 名词定义

名词 含义
BBinder 服务端接口
BpBinder 客户端接口

2. 使用Binder流程

  • 开发native service
    1. 定义服务端接口文件,即IPmcsService类,其继承于IInterface类
    2. 定义BnPmcsService类,继承于BnInterface
    3. 实现一个PmcsService类,继承于BnPmcsService类,并实现响应客户端请求onTransact()
  • 开发client

BpBinder(客户端)如何把请求发送给BBinder(服务端),BBinder又如何将响应传回给客户端的呢?

3. BBinder服务端

服务端类

  • BBinder 实现了大部分的IBinder 接口,除了onTransact() 和 queryLocalInterface(), getInterfaceDescriptor()
  • IInterface
    • 实现queryLocalInterface(),该函数可查询服务端提供的服务
    • INTERFACE模板继承和实现IInterface
  • BnInterface
    • BnInterface 对象将自己强制转换成 IInterface对象返回给客户端
    • BnInterface是一个模板类,继承了BBinder和模板INTERFACE
    • BnInterface 实现了IBinder的queryLocalInterface()和getInterfaceDescriptor()
  • BnPmcsServer同时继承IInterface和IBinder,定义了onTransact(),没有实现
  • PmcsService里具体实现了onTransact()

4. BpBinder客户端

​ 目标:找到一个类,它必须同时拥有IBinder 和 IIterface的特性

client

  • BpRefBase里有IBinder成员变量
  • BpInterface
    • BpInterface继承于BpRefBase,而BpRefBase里有IBinder成员变量,从而获取到IBinder特性
    • BpInterface继承于INTRFACE,这里INTRFACE就是IPmcsService
  • IInterface中函数asBinder()->BpInterface::onAsBinder()->BpInterface::remote(),返回得到mRemote
  • BpPmcsService为客户端,具体向服务端发起请求
  • PmcsProxy为客户端的回调,服务端响应完请求后,调用回调向调用发起者通知执行结果

BpRefbase的mRemote是如何被赋值的?

最后追溯到 IPmcsService.cpp

1
interface_cast<IMediaPlayer>(reply.readStrongBinder()); //reply里读出IBinder,然后转成IMediaPlayer接口对象

通过子类一级一级注入,即new一个BpPmcsService,然后将其转换为父父类IPmcsService

客户端流程

​ 客户端从远端生成一个IBinder对象,再生成BpPmcsService,将其转换为IpmcsService接口对象,调用其接口方法,最终调用到BpBinder的transact()

​ 而客户端的transact()函数通过Binder Driver获取到BBinder对象(IBinder的实现),并调用onTransact()

5. 总流程

pmcs

  • 应用程序的角度
    1. 已知服务名,APP通过getService() 从ServiceManager 获取存储在Parcel对象的服务的信息
    2. APP收到Parcel对象 (通过kernel),获取到服务对应的服务号,mHandle
    3. 以mHandle作为参数输入生成一个IBinder对象(实际是BpBinder)
    4. APP获取到IBinder对象,通过asInterface(IBinder)生成服务对应的Proxy对象(BpPmcsService),将其强转到*IPmcsService,调用接口函数
    5. 所有的接口对象调用最终会走到BpBinder->transact()函数,这个函数调用IPCThreadState->transact()并以Service号作为参数之一
    6. 最终通过系统调用ioctrl() 进入内核空间,Binder驱动根据传进来的Service 号寻找该Service正处于等待状态的Binder Thread, 唤醒它并在该线程内执行相应的函数,并返回结果给APP

图像处理算法

发表于 2020-05-06 | 分类于 图像处理 | 阅读次数

A.模糊(滤波)

1.归一化盒子滤波(BoxFilter)

  • 主要功能:在给定的滑动窗口下,对每个窗口内的像素值进行快速相加求和

  • 缺点:无法克服边缘像素信息丢失缺陷,原因是均值滤波是基于平均权重

  • 原理:

    1. 给定图像,宽高为(M,N),确定待求矩形模板的宽高(m,n),如图紫色矩形。图中黑色方块代表像素点,红色方块代表假象像素。
    2. 开辟一段大小为M的数组,记为buff,用来储存计算过程中的,存在红色假象像素点中
    3. 将矩形模板(紫色)从左上角,从左往右,从上往下单元素移动,每移动到新位置,计算矩形内的像素和保存在数组A(输出图片)中。以(0,0)位置为例:
      • 将绿色矩形内的每一列像素求和,结果放在buff内(红色方块)
      • 对蓝色矩形内的像素点求和,结果即为紫色特征矩形内的像素和,把它存放到数组A
    4. 每次紫色矩形向右移动时,实际上就是求对应的蓝色矩形的像素和,此时只需把上次的求和结果减去蓝色矩形内的第一个红色快,再加上其右边的红色块,就是当提前位置的和,公式表达为:sum[i] = sum[i-1] - buf[x-1] + buff[x+m-1]
    5. 当紫色矩形移动到行末时,需对buff进行更新。因整个绿色矩形下移一个像素,所以对于每个buff[i]需要加一个新进来的像素,再减去一个出去的像素,开始新一行的计算
  • 此处为原理流程,源码见代码部分

2.高斯滤波

  • 功能:
  • 缺点:部分克服边缘像素信息丢失缺陷,但无法完全避免,因为没有考虑像素值的不同
  • 原理:

3.中值滤波

  • 功能:
  • 缺点:部分克服边缘像素信息丢失缺陷,但无法完全避免,因为没有考虑像素值的不同
  • 原理:

4.双边滤波

  • 功能:
  • 优缺点:避免边缘信息丢失,保留图像轮廓不变
  • 原理:

B.形态学操作

1.定义

形态学定义 函数定义 原理 应用
膨胀 dilate 取领域内最大值作为中心点新像素值 前景为黑,变小;前景为白,变大
腐蚀 erode 取领域内最小值作为中心点新像素值 前景为黑,变大;前景为白,变小
开操作 open 先腐蚀后膨胀(注意:结构元素需设置为包含部分目标对象) 假设对象是前景色,背景是黑色,可以去掉小的对象
闭操作 close 先膨胀后腐蚀 假设对象是前景色,背景是黑色,可以填补小的洞
形态学梯度 Morphological Gradient 膨胀减腐蚀,dst = morph(src, element) = dilate(src, element) - erode(src, element) 又称为基本梯度(其他还包括内部梯度、方向梯度)
顶帽 top hat 源图像与开操作之间的差值图像
黑帽 black hat 闭操作与源图像的差值图像

2.提取水平与垂直线

  • 提取步骤
    • 输入彩色图像-imread
    • 转换为灰度图像-cvtColor
    • 转换为二值图像-adaptiveThreshold
    • 定义结构元素
    • 开操作(腐蚀+膨胀)提取水平与垂直线
  • 启示:
    • 卷积核可根据需要更改形状,从原图提取想要的形状
    • 提取文字、图像(去除干扰点),可用不同卷积核

C.图像上采样和降采样

  • 图像金字塔:
    • 高斯金字塔:用来对图像进行降采样
      • 从底向上,逐层降采样得到
      • 降采样之后,图像大小是原图MN的M/2 N/2(1/4),即对原图像删除偶数行与列,得到降采样后上一层的图片
      • 两步生成过程:
        • 对当前层进行高斯模糊
        • 删除当前层的偶数行与列
    • 高斯不同:
      • 定义:把同一张图像在不同的参数下做高斯模糊之后的结果相减,得到的输出图像成为高斯不同(DOG)
      • 高斯不同是图像的内在特征,在灰度图像增强‘角点检测中常用到
    • 拉普拉斯金字塔:用来重建一张图片,根据它的上层降采样图片

D.基本阈值操作

  • 图像阈值
    • 定义:图像分割的标尺
    • 阈值产生的算法
      • THRESH_OTSU
      • THRESH_TRIANGLE
    • 阈值类型
      • 阈值二值化(threshold binary):大于取阈值,小于取零
      • 阈值反二值化(threshold binary inverted):大于取零,小于取阈值
      • 截断(truncate):大于处取阈值,小于不变
      • 阈值取零(threshold to zero):大于不变,小于取零
      • 阈值反取零(threshold to zero inverted):大于取零,小于不变

E.自定义线性滤波

  • 卷积概念
    • 卷积是图像处理中一个操作,是kernrl在图像的每个像素上的操作
    • kerner本质上是一个固定大小的矩阵数组,其中中心点为锚点(anchor point)
    • 求锚点周围覆盖的像素乘积之和(包括锚点),除以卷积数组大小,用来替代锚点下像素点值
  • 常见算子
    • Robert算子:2*2,{1, 0, 0, -1}或{0, 1, -1, 0},寻找梯度、边缘
    • Sobel算子:3*3,{-1, 0, 1, -2, 0, 2, -1, 0, 1}或{-1, -2, -1, 0, 0, 0, 1, 2, 1},常用于边缘检测
    • 拉普拉斯算子:3*3,{0, -1, 0, -1, 4, -1, 0, -1, 0},寻找梯度、边缘
  • 自定义卷积模糊

F.边缘处理

  • 卷积边缘问题:
    • 图像卷积的时候边界像素不能被卷积操作
    • 原因在于边界像素没有完全跟kernel重叠
  • 边缘处理
    • 在卷积开始之前增加边缘像素,填充像素值为0或RGB黑色
    • opencv中默认方法为:BORDER_DEFAULT
      • BORDER_CONSTANT - 用指定像素值填充边缘
      • BORDER_REPLICATE - 用已知的边缘像素值填充边缘像素
      • BORDER_WRAP - 用另外一边的像素来补偿填充

G.sobel算子

  • 卷积应用-图像边缘提取
    • 图像像素值渐变或突变,求导取最高点即为跃迁点(变化最大)
  • Sobel算子
    • Sobel算子:3*3,{-1, 0, 1, -2, 0, 2, -1, 0, 1}或{-1, -2, -1, 0, 0, 0, 1, 2, 1},常用于边缘检测
    • 是离散微分算子(discrete differentiation operator),用来计算图像灰度的近似梯度
    • Sobel算子功能集合高斯平滑和微分求导
    • 又称为一阶微分算子,求导算子,在水平和垂直两方向求导,再平方和开根号(绝对值之和),得到(图像x与y方向)梯度图像
  • Schar函数
    • 求取导数的近似值,kernel=3时不准确,opencv使用改进版本Scharr函数,3*3,{-3, 0, 3, -10, 0, 10, -3, 3}或{-3, -10, 3, 0, 0, 0, 3, 10, 3}
  • 代码流程
    • 高斯平滑:使图像像素平滑变化
    • 转灰度
    • 求梯度x和y
    • 混合xy
  • 个人理解:
    • 用卷积和扩大水平/垂直像素差异

H.laplance算子

  • 原理
    • 比sobel算子更进一步,这里采用 二阶导为零处即为像素值变化最大处
  • 流程
    • 高斯模糊-去噪声GaussianBlur()
    • 转灰度cvtColor()
    • 拉普拉斯-二阶导计算Laplance()
    • 取绝对值convertScaleAbs()

I.Canny边缘检测

  • 流程:
    • 高斯模糊GaussianBlur
      • 将高斯算子与原图像卷积
    • 灰度转换cvtColor
    • 计算梯度Sobel/Scharr:像素值差异的地方
    • 非最大信号抑制
      1. 将当前像素的梯度强度与沿正负方向梯度方向上的两个像素比较
      2. 若当前像素的梯度强度与另外两个像素相比最大,则该像素点保留位边缘点,否则该像素点会被抑制
    • 高低阈值输出二值图像:抑制噪声引起的弱边缘

J.霍夫变换

  • 霍夫直线变换

    • 简介
      • 用来做直线检测
      • 前提条件:边缘检测已完成
      • 平面空间到极坐标空间转换
    • 原理
      • 图形原理在notability中,后续整理
      • r = x cos(theta) + y sin(theta)
        1. canny边缘检测
        2. 逐像素点进行霍夫空间变换:由像素坐标系(x,y)变换到笛卡尔坐标系(r, theta)
        3. 局部非极大值抑制:去除噪声点
        4. 对笛卡尔坐标系中的点排序
        5. 求得笛卡尔坐标系中点极大值对应的角度和距离
        6. 将笛卡尔坐标转为像素坐标,画线
  • 霍夫圆变换(21HT)

    • $r^2 = (x-a)^2 + (y-b)^2$

    • 第一阶段:检测圆心

      • (中值滤波:去除噪声)
      1. canny边缘检测
      2. sobel计算图形梯度,并确定圆周线,其中圆周的梯度就是其法线
      3. 在二维霍夫空间内,绘出所有图形的梯度直线,某坐标点上累加和的值越大,说明在该点上直线相交的次数越多,也就是越有可能是圆心
      4. 在霍夫空间的4邻域内进行非最大值抑制
      5. 设定一个阈值,霍夫空间内累加和大于该阈值的点就对应于圆心
    • 第二阶段:检测半径

      1. 计算某一个圆心到所有圆周线的距离,这些距离中就有该圆心所对应的圆的半径的值,这些半径值当然是相等的,并且这些圆半径的数量要远远大于其他距离值相等的数量
      2. 设定两个阈值,定义为最大半径和最小半径,保留距离在这两个半径之间的值,这意味着我们检测的圆不能太大,也不能太小
      3. 对保留下来的距离进行排序
      4. 找到距离相同的那些值,并计算相同值的数量
      5. 设定一个阈值,只有相同值的数量大于该阈值,才认为该值是该圆心对应的圆半径
      6. 对每一个圆心,完成上面的2.1~2.5步骤,得到所有的圆半径

K.像素重映射

  • 定义:g(x, y) = f(h(x, y))
    • 把输入图像中各个像素按照一定规则映射到另一张图像对应位置
  • cv::remap()

L.直方图均衡化

  • 图像直方图
    • 定义:统计整个图像在灰度范围内的像素值(0-255)出现的频率次数
    • 反映:图像灰度的分布情况
  • 流程
    1. 计算原图像素值概率
    2. 从小到大累加像素值概率
    3. 用 累加概率 * 像素最大值 之积 替换原图像像素值
  • cv::equalizeHist(src, dst)
    • 输入为灰度图
  • 应用:提高图像对比度、拉伸图像灰度值范围,同时图像的细节更为丰富

Learning OpenCV3

发表于 2020-05-06 | 分类于 图像处理 | 阅读次数

Learning OpenCV3

一、Overview

1.计算机视觉概念

类比于人在道路两侧停放的车中寻找特定的车,图像信号通过眼睛传输到大脑,大脑有注意力识别系统:通过任务依赖的方式,检查图像重要区域,抑制其他区域。利用人生活多年来形成的交叉关联的感觉识别重要信息,再由大脑的反馈回路返回到传感器(眼睛肌肉),进一步去获取需要的图像。

###2.遇到的问题

用2D去描绘3D世界的误差,数据的失真和噪声破坏。具体如下:

  • 上下文信息:

    • 例子:

      机器人在房子里捡起订书机,首先机器人可能会用到以下事实:办公桌是办公室内的对象,而订书机是最大可能在办公桌上找到,这是一个隐式的大小位置参考;且机器人需要忽略相同大小形状的小木盒、以及墙画上的订书机图片。

      相反,对于图像检索任务,机器人查询的数据库里都是真实的订书机,很可能就只拍摄了正常尺寸的订书机;在拍摄这些图片时,也隐藏这很多无意的隐式信息。

    • 解决:

      使用机器学习进行显示建模,对隐藏变量(大小、形状、重力方向等)用与他们值相关的标签训练集中进行校正。或者使用其他传感器测量隐藏的偏差分量。

  • 噪音

    • 例子:

      仅通过几个点比较无法检查出物体边缘,有些像素点可能在生成、传输的途中遭到破坏,不能准确识别。

    • 解决

      统计局部区域可较准确的进行边缘检测

      通过建立直接从数据中学到的显式模型来描述,如人们已经很了解镜头畸变,只需建立一个多项式模型参数,通过调整参数从而更正失真。

二、opencv入门示例

简单的入门示例,这里有一个正好前段时间涉及到的边缘检测算法(二值化->区分前后景),效果如下:

​ 阈值为1、10,可以看到边缘检测很不清晰,把很多rgb值高的点也包含进来

阈值为1,10

​ 阈值为10、100,可以看到颜色较重的区域也能区分出来

阈值10,100

​ 阈值为100、500,这样小鱼的边缘就很清晰了

阈值100,500

三、OpenCV数据类型

从组织的角度来看,数据分为以下三类:

  • c++原语(int, float等):简单向量和矩阵、简单几何概念(点、矩阵、尺寸等)
  • 辅助对象:抽象概念,如垃圾回收指针、切片对象、终止条件等
  • 大数组类型

1.基本类型

  • Point

    基于模板结构实现的,可以是任何类型的点

    • 分类:

      • 二维、三维、四维点
      • cv::Point2i,最后一个字母表示构造点的原语类型。(b:无符号字符,s:短整型,i:32位整数,f:32位浮点数,d:64位浮点数)
    • 操作:

      | 运行方式 | 例子 |
      | :——————: | :———————–: |
      | 默认构造函数 | cv::Point2i p; |
      | 复制构造函数 | cv::Point3f p2(p1) |
      | 值构造函数 | cv::Point3d p(x0, x1, x2) |
      | 转换为固定向量类 | (cv::Vec3f) |
      | 访问成员 | p.x; |
      | 点乘 | float x = p1.dot(p2); |
      | 双精度点乘 | double x = p1.ddot(p2); |
      | 叉乘 | p1.cross(p2); |
      | 查询点p是否在矩形r里 | p.inside(r); |

    • cv::Scalar类

      四维点类,所有成员时双精度浮点数

      | 运行方式 | 示例 |
      | :—————: | :————————————————-: |
      | 默认构造函数 | cv::Scalar s; |
      | 复制构造函数 | cv::Scalar s2(s1); |
      | 值构造函数 | cv::Scalar s(x0, x1, x2, x3); |
      | 逐元素乘法 | s1.mul(s2); |
      | (四元数)共轭 | s.conj(); // (return cv::Scalar(s0, -s1, -s2, -s3)) |
      | (四元数)real test | s.isReal(); // (returns true if s1==s2==s3==0) |

      该类操作强制转换到固定向量类,与point类不同,cv::Scalar直接从固定向量类模板的实例化中继承

  • size

    size类类似于point类,类型有Size、Size2i、Size2f,唯一区别就是命名区别(x、y与width、height)

    | 运行方式 | 示例 |
    | :———-: | :——————: |
    | 默认构造函数 | cv::Size sz; |
    | 复制构造函数 | cv::Size sz2(sz1); |
    | 值构造函数 | cv::Size2f sz(w, h); |
    | 访问成员 | sz.width; sz.height; |
    | 计算面积 | sz.area(); |

  • Rect

    矩形类包含point类的成员x,y(代表矩形左上角坐标),和宽高类(矩形的大小),但矩形不继承点类

    | 运作方式 | 示例 |
    | :————————–: | :——————————-: |
    | r1、r2矩形的交点 | cv::Rect r3 = r1&r2; r1 &= r2; |
    | 包含矩形r1、r2的最小面积矩形 | cv::Rect r3 = r1 ! r2; r1 |= r2; |
    | 用x平移矩形r | cv::Rect rx = r + x; r += x; |
    | 将矩形r按大小s增大 | cv::Rect rs = r + s; r += s; |
    | 比较矩形是否相等 | bool eq = (r1 == r2); |
    | 比较矩形是否不 | bool eq = (r1 != r2); |

  • RotatedRect

    c++ Opencv接口中为数不多的没有模板的类之一,相反,这是一个容器,保存了一个cv::Point2f(中心),一个cv::Size2f(大小)和一个额外的浮点数(角度),角度表示矩形围绕中心的旋转

    | 运行方式 | 示例 |
    | :————————: | :—————————-: |
    | 默认构造函数 | cv::RotatedRect rr(); |
    | 复制构造函数 | cv::RotatedRect rr2(rr1); |
    | 从两个角度构建 | cv::RotatedRect(p1, p2); |
    | 值构造函数(点,大小,角度) | cv::RotatedRect(p, sz, theta); |
    | 访问成员 | rr.center; rr.size; rr.angle; |
    | 返回角列表 | rr.points(pts[4]); |

  • fixed matrix

    固定矩阵类是指在编译时位数已知的矩阵,其数据的所有内存都分配在堆栈上,意味着可以快速地分配和清理。在c++ Opencv接口中,固定矩阵类实际上是一个模板,是许多其他基本类的核心(固定向量类派自固定矩阵类,其他类则派生自固定向量类)。

    | 运作方式 | 示例 |
    | :——————————: | :———————————————————-: |
    | 默认构造函数 | cv::Matx33f m33f; cv::MAtx43d m43d; |
    | 复制构造函数 | cv::Matx22d m22d(n22d); |
    | 值构造函数 | cv::Matx21f m(x0, x1); |
    | 相同元素的矩阵 | m33f = cv::Matx33f::all(x); |
    | 零矩阵 | m23d = cv::Matx23d::zeros(); |
    | 一个矩阵 | m16f = cv::Matx16f::ones(); |
    | 创建单位矩阵 | m33f = cv::Matx33f::eye(); |
    | 创建一个可容纳另一个对角线的矩阵 | m31f = cv::Matx33f:diag(); //31的矩阵 |
    | 创建一个具有均匀分布项的矩阵 | m33f = cv::Matxf::randu(min, max); |
    | 创建一个具有正态分布条项的矩阵 | m33f = cv::Matx33f::nrandn(mean, virance); |
    | 访问成员 | m(i, j); m(i); |
    | 矩阵代数 | m1 = m0; m0
    m1; m0 + m1; m0 -m1; |
    | 单例代数 | m a; a m; m / a; |
    | 比较 | m1 == m2; m1 != m2; |
    | 点积 | m1.dot(m2); //m的精度 |
    | 点积 | m1.ddot(m2); //双精度 |
    | 重塑矩阵 | m91f = m33f.reshape<9, 1="">(); |
    | 类型转换 | m44f = (Matx44f)m44d; |
    | 提取子矩阵 | m44f.get_minor<2,2>(i,j); |
    | 提取第i行 | m14f = m44f.row(i); |
    | 提取第j列 | m14f = m44f.col(j); |
    | 提取矩阵对角线 | m41f = m44f.diag(); |
    | 计算转置 | n44f = m44f.t(); |
    | 求逆矩阵 | n44f = m44f.inv(method); //默认方法为cv::DECOMP_LU |
    | 解线性方程组 | m31f = m33f.solve(rhs31f, method); m32f = m33f.solve2(rhs32f, method); // 模板表单,默认方法为DECOMP_LU |
    | 逐元素乘法 | m1.mul(m2); |

    注意,许多固定矩阵函数相对于类都是静态的(作为类的成员而不是特定的成员直接访问他们),如构造3*3的矩阵,cv::Mat33f::eye()。

  • fixed vector

    固定向量类是从固定矩阵类派生的,可以看作cv::Matx<>的便捷功能。

    | 运作方式 | 示例 |
    | :———-: | :——————-: |
    | 默认构造函数 | Vec2s v2s; Vec6f v6s; |
    | 复制构造函数 | Vec3s u3f(v3f); |
    | 值构造函数 | Vec2f v2f(x0, x1); |
    | 访问成员 | v4f[i]; v3w(j); |
    | 向量叉乘 | v3f.cross(u3f); |

    主要便利是使用单顺序访问元素

  • complex number

    复数类与STL复合体关联的类最实质的区别在于:在STL类中,通过real()和image()访问实数和虚数;而在OpenCV类中,直接访问公共成员变量re和im

2.辅助对象

  • TermCriteria

    停止条件

  • Range

    范围

  • Ptr模板和垃圾回收

    c++中智能指针,该指针是我们能创建对对象的引用,所有引用被计数,当引用超出范围时,智能指针的引用计数将减少,一旦所有引用(指针的实例)减到0,实例对象将被自动清除(已分配)。

    与c++中智能指针类似,为想要“包装”的类对象实例化一个指针模板(cv::Ptr p = makePtr()),模板对象的构造函数指向对象的指针。此操作后,智能指针p可像普通指针一样传递和使用(即,支持运算符operator*()和operator->()),可创建其他相同类型的对象,而不需要向它们传递新对象的指针。如:Ptr q,并将p的值赋给q。这里只有一个实际的p和q指向的cv::Mat33f对象,p和q都知道他们各自为1.若p消失(超出范围),则q知道它时唯一剩下的对源实矩阵的引用;若q消失,且其析构函数被调用,q就知道它时唯一引用的指针,q会取消分配原始矩阵。

  • Exception类和异常处理

  • DataType

    当OpenCV库函数需要传递特殊概念的数据类型时,可通过创建cv::DataType<>类型的对象来实现。传递的实际对象时这个模板的实例对象。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    // OpenCV中DataType的定义
    template<> class DataType<float>
    {
    public:
    typedef float value_type;
    typedef value_type work_type;
    typedef value_type channel_type;
    typedef value_type vec_type;

    enum {
    generix_type = 0,
    depth = DataDepth<channel_type>::value,
    channels = 1,
    fmt = DataDepth<channel_type>::fmt,
    type = CV_MAKETYPE(depth, channels)
    };
    };
  • InputArray和OutputArray

    输入、输出对象

3.实用函数

除了上述专用原始数据类型,OpenCV还提供一些有效处理数学运算和其他运算的专用功能,该使用功能包含数学工具、测试、错误生成、内存和线程处理、优化等

函数 功能
cv::aliginPtr() 将指针对齐到给定的字节数
cv::aliginSize() 将缓冲区大小与给定的字节数对齐
cv :: allocate() 分配c样式的对象数组
cvCeil() 将浮点数向上取整
cv::cubeRoot() 计算立方根
cv::CV_Assert() 若给定条件不成立,则引发异常
CV_Error() 宏以构建cv :: Exception (从固定字符串)并将其抛出
CVErrpr() 宏来构建cv :: Exception (从格式化的字符串)并抛出它
cv::deallocate() 取消分配c样式的对象数组
cv::error() 提示错误并引发异常
cv::fastAtan2() 计算向量的二维角度
cv::fastFree() 取消分配内存缓冲区
cv::fastMalloc() 分配对齐的内存缓冲区
cvFloor() 将浮点数向下取整
cv::format() 使用类似sprintf的格式,创建STL字符串
cv::getCPUTickCount() 从内部cpu计时器获得滴答计数
cv::getNumThreads() 计算OpenCV当前使用的线程
cv::getOptimalDFTSize() 为计划传递给cv::DFT()的数组计算最佳大小
cv::getThreadNum() 获取当前线程的索引
cv::getTickCount() 从系统获取滴答计数
cv::getTickFrequency() 每秒获取数字/刻度
cvIsInf() 检查浮点数x是否为无穷大
cvIsNan() 检查浮点数是否为“非数字“
cvRound() 将浮点数舍入到最近的整数
cv::setNumThreads() 设置OpenCV使用的线程数
cv::setUseOptimized() 启/禁用代码优化
cv::useOptimized() 指示代码优化启用的状态

具有旧版界面的功能,为c定义的。

4.模板结构

四、图像和大数组类型

1.动态和可变存储

  • cv::Mat类:N维密集数组

    cv::mat类可用于任何数目维度的阵列,数据储存在数组中,可以认为是一个n维“光栅扫描”的模拟。每个矩阵包含一个表示数组内容的flags元素、一个表示维度的dims元素、行列元素、一个表示位置的数据指针、以及一个引用计数器 .可把数组理解为.

  • 生成一个数组

    实例化一个cv::Mat类,并通过其成员函数create()来分配数据,其所需参数:行数、列数、类型、以及数组代表二维对象的配置。有效数组类型指定元素的基本类型和通道数量,所有这些类型都在库头文件中定义,其形式为:CV_{8U,16S,16U,32S,32F,64F}C{1,2,3}.如:CV_32FC3代表一个32位浮点三通道数组.

    1
    2
    3
    4
    5
    6
    7
    8
    //示例

    cv::Mat m;
    m.create(3, 10, CV_32FC3); // 为一个3行10列3通道的32位浮点数组分配数据
    m.setTo(cv::Scalar(1.0f, 0.0f, 1.0f)); // 三通道值分别为1.0, 0.0, 1.0

    // 等效上三行
    cv::Mat m( 3, 10, CV_32FC3, cv::Scalar( 1.0f, 0.0f, 1.0f ) );
    • 构造函数

      | 函数 | 功能 |
      | :———————————————————-: | :—————————————: |
      | cv::Mat(); | 默认构造函数 |
      | cv::Mat(int rows, int cols, int type); | 二维数组 |
      | cv::Mat(int rows, int cols, int type, const Scalar& s); | 带初始值的二维数组 |
      | cv::Mat(int rows, int cols, int type, void data, size_t step=AUTO_STEP); | 具有类型的二维数组,预先存在的数据 |
      | cv::Mat(cv::Size sz, int type); | 二维数组,sz大小 |
      | cv::Mat(cv::Size sz, int type, const Scalar& s); | 带初始值的二维数组,sz大小 |
      | cv::Mat(cv::Size sz, int type, void data, size_t step=AUTO_STEP); | 具有类型的二维数组,预先存在的数据,sz大小 |
      | cv::Mat(int ndims, const int
      sizes, int type); | 多维数组 |
      | cv::Mat(int ndims, const int sizes, int type, const Scalar& s); | |
      | cv::Mat(int ndims, const int
      sizes, int type, const Scalar& s, size_t step=AUTO_STEP); | |

  • 复制构造函数

    | 函数 | 功能 |
    | :———————————————————-: | :———————————————–: |
    | cv::Mat(const Mat& mat); | 复制构造函数 |
    | cv::Mat(const Mat& mat, const cv::Range& rows, const cv::Range& cols); | 复制构造函数,仅能复制行和列 |
    | cv::Mat(const Mat& mat, const cv::Rect& roi); | 复制构造函数,仅能复制感兴趣区域(roi)的行和列 |
    | cv::Mat(const Mat& mat, const cv::Range* ranges); | 通用区域复制构造函数,复制一块范维数组 |
    | cv::Mat(const cv::MatExpr& expr); | 复制用其他矩阵的代数表达式的结果初始化m的构造函数 |

  • 模板构造函数

    | 函数 | 功能 |
    | :———————————————————-: | :—————————————————–: |
    | cv::Mat(const cv::vec& vec, bool copyData = true); | 从同一类型的cv::Vec中构造一个类型为T、大小为n的一维数组 |
    | cv::Mat(const cv::Matx& vec, bool copyData = true); | 从同一类型的cv::Matx中构造一个尺寸为m×n的T型二维数组 |
    | cv::Mat(const std::vector& vec, bool copyData = true); | 从包含相同类型元素的STL向量构造T类型的一维数组 |

  • 静态函数

    | 函数 | 功能 |
    | :——————————–: | :——————————————————: |
    | scv::Mat::zeros(rows, cols, type); | 创建rows行cols列的cv::Mat,初始全为0,类型为type |
    | scv::Mat::ones(rows, cols, type); | 创建rows行cols列的cv::Mat,初始全为1,类型为type |
    | scv::Mat::eye(rows, cols, type); | 创建rows行cols列的cv::Mat,初始为identity矩阵,类型为type |

  • 分别访问数组元素

    • 几种访问函数

      | 函数 | 功能 |
      | :——————-: | :——————————–: |
      | M.at(i); | 整数数组M中第i个元素 |
      | M.at(i, j); | 浮点数组M中(i,j)位置元素 |
      | M.at(pt); | 整数矩阵M中(pt.x, pt.y)位置的元素 |
      | M.at(i, j, k); | 三维浮点数组M中(i, j, k)位置的元素 |
      | M.at(idx); | uchar数组M中idx[]位置的元素 |

      还可通过怕c样式的指针访问二维数组

      对于矩阵,有两种方法获取其数据指针:

      • 成员函数ptr<>()
      • M.at<>()
  • N元数组迭代器:NAryMatlterator

    一次遍历多个数组,返回数组块,称为planes,常用于多数组计算(多点代表面)

  • 按快访问数组元素

未完待续!

c知识点

发表于 2019-08-24 | 分类于 c++ | 阅读次数

知识点总结

  • 知识点总结
    • 数据存放位置
    • c++内存模型
    • class
    • const
    • static
    • &
    • 初始化
    • volatile
    • template
    • virtual
    • 构造/析构函数
    • operator
    • 指针、引用
    • 双指针、指针引用
    • 条件编译
    • 资源管理
      • 提供对原始资源的访问

数据存放位置

  1. 本地变量:栈
  2. new出的对象:堆
  3. static/全局变量:全局数据区

c++内存模型

  • 存放对象的地方
    1. 栈
    2. 堆
    3. 全局数据区
  • 访问对象的方式
    1. 变量访问对象
    2. 指针访问对象
    3. 引用访问对象

class

  • struct默认public
  • class默认private
  • 其他用法完全一样,都包含成员函数、继承、多态

const

  • 只需看其右侧最近的类型
    • *前后
      • 前:指针指向变量
      • 后:指针本身是变量
    • 函数前后
      • 前:函数返回值为const
      • 后:函数成员为const
      • 最后:此成员函数不改变类中的成员变量(mutable可变的)

static

  • 静态局部变量:
    • 全局数据区分配内存
    • 随函数第一次调用时初始化,程序结束时销毁
    • 声明处初始化,若无显式初始化,会自动初始化为0
    • 作用域为局部作用域
  • 静态全局变量:
  • 静态函数:
    • 本文件可见(文件隔离)
  • 静态数据成员:
    • 只有class有一份,而普通数据成员是每一个实例有一份
  • 静态成员函数:
    • 静态成员函数不能访问非静态(成员函数、数据成员)
    • 非静态成员函数可以访问静态(成员函数、数据成员)
    • 解释:静态是属于类的,它不知道创建了多少对象;而对象中的数据对类中数据一清二楚

&

  • 引用
    • 在赋值=右侧
    • 和变量在一起
  • 取地址
    • 在赋值=左侧
    • 和类型在一起

初始化

  • A::A(int m, int n) : x(m), y(n) {} //初始化
  • A::A(int m, int n){x = m; y = n;} //赋值
  • singleton模式:local static对象替换non-local static对象
    • 基础在于:c++保证,函数内的local static对象会在”该函数被调用期间“”首次遇上该对象之定义式“时被初始化。

volatile

  • 声明的变量可能随时变化,遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问
  • 从所在的内存读取数据,即使它前面的指令刚刚从该处读取过数据。而且读取的数据立刻被保存。
  • 应用在共享变量

template

// TODO:

virtual

  • virtual析构函数用于多态基类的声明
  • 被用作base class的类声明为virtual析构函数,其他类不要声明virtual析构函数(对象体积增大,不能将其传给其他语言写的函数-无移植性)
  • 类的设计目的如果不是base class或具有多态性,就不该声明virtual析构函数
  • 别让异常逃出析构函数
    • 结束程序 try{…}catch{…;std::abort;}
    • 吞掉异常  try{…}catch{…;}
    • 前两者的优化: 

构造/析构函数

  • 构造函数调用顺序:base -> derived
  • 析构函数调用顺序:derived -> base
  • 在构造/析构函数中不要调用虚函数,因为这类的调用不会下降至derived class(相对的)

operator

  • 令重载操作符返回一个reference to *this
  • 处理自我赋值(赋值时两个为同一个对象,删除一个就都删除了)
    • 证同测试,并跳过处理流程
    • 用副本去操作
    • copy and swap
  • copy函数
    • 确保复制”对象内的所有成员变量” 和 “所有base class成分”
    • 不要用一个copy函数实现另一个copy函数,应该将共同机能放入第三函数,并由两个函数共同调用

指针、引用

  • 指针
    • 概述:指针传递参数本质上是值传递的方式,其传递一个地址值
    • 流程:值传递的过程中,被调函数的形参作为被调函数的局部变量处理,即在栈中开辟了内存空间以存放由主调函数放进来的实参的值,从而成为实参的一个副本
    • 特点:被调函数对形参的任何操作都是作为局部参数进行,不影响主调函数是参变量的值
    • 应用:
      • 不改变指针指向,形参指向实参,可操作实参
      • 改变指针指向,则形参作为副本,与实参无关,不可操作实参
  • 引用
    • 概述:引用传递本质是地址传递,传递的是实参变量的地址
    • 流程:被调函数对形参的任何操作都被处理成间接寻址,即通过栈中存放的地址访问主调函数中的实参变量。正因为如此,被调函数对形参做的任何操作都影响了主调函数中的实参变量。
  • 区别
    • 指针传递参数时,指针中存放的也是实参的地址,但是在被调函数内部指针存放的内容可以被改变,即可能改变指向的实参,所以并不安全
    • 而引用则不同,它引用的对象的地址一旦赋予,则不能改变

双指针、指针引用

// TODO:

条件编译

  • 当标识符定义过了,编译程序段1,否则编译程序段2
    1
    2
    3
    4
    5
    #ifdef 标识符
    程序段1
    else
    程序段2
    #endif

资源管理

  • 以对象管理资源
    • 为防止资源泄露,请使用RAII对象(tr1::shared_ptr、auto_ptr),它们在构造函数中获得资源并在析构函数中释放资源

提供对原始资源的访问

  • 问题:如何调用一个对象

    1
    2
    3
    std::tr1::shared_ptr<Investment>pInv(createInvestment());
    int daysHeld(const Investment* pi); // 返回投资天数
    int days = daysHeld(pInv); // 调用出错

    编译不通过,daysHeld需要Investment*指针,传入的却是类型为tr1::shared_ptr的对象

  • 解决办法:

    • 智能指针
      • 显示转换: trl::shared_ptr和auto_ptr都提供了一个get成员函数,它会返回只能指针内部原始资源指针(的复件)
      • 隐式转换: 利用重载了的取值操作符(operator->和operator*),其允许隐式转换至内部原始指针
    • 类

      • 1
        2
        3
        4
        5
        6
        7
        8
        9
        FontHandle getFont();   //API
        void releaseFont(FontHandle fh); // API
        class Font {
        public:
        explicit Font(FontHandle fh) : f(fh){} // 获取资源,pass-by-value
        ~Font() {releaseFont(f);} // 释放资源
        private:
        FontHande f; // 原始字体资源
        };

        font类中添加两种解决办法

      • 显式转换

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        class Font {
        public:
        ...
        FontHandle get() const {retun f;} //显示转换
        ...
        };

        //调用
        void changeFontSize(FontHandle f, int newSize); // API
        Font f(getFont());
        int newFontSize;
        cangeFontSize(f.get(), newFontSize); //显示将Font转换为FontHandle

        问题: 增加泄漏字体的可能性,而Font类的主要设计目的是为了防止资源(字体)泄露

      • 隐式转换

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        Class Font {
        public:
        ...
        operator FontHandle() const {return f;} // 隐式转换
        ...
        };

        // 调用
        Font f(getFont());
        int newFontSize;
        changeFontSize(f, newFontSize); // 将Font隐式转换为FontHandle

        问题:

        1
        2
        3
        Font f1(getFont());
        FontHandle f2 = f1; // 原意是要拷贝一个Font对象
        //却反将f1隐式转换为FontHandle对象,然后才复制它

所为所得

发表于 2019-08-13 | 分类于 随想 | 阅读次数
「密码不能告诉你」
阅读全文 »

clox编译器

发表于 2019-07-19 | 分类于 c | 阅读次数

clox

(持续更新中,目前到scanner)

clox是由Bob Nystrom基于c编写的编译器,最终解释结果编译成字节码,通过编写学习,可以了解编译器的基本原理
主流程主要分为三步:

  1. 扫描
  2. 编译
  3. 执行(虚拟机)

github代码库:zaizizaizai

推荐:原文教程请戳这里

极度推荐项目库:project-based-learning

目录结构

  • 文件说明
  • 流程
    • clox编译器整个代码逻辑流程
    • 执行减法(3-1)的流程
  • 结构
    • chunk结构
    • ValueArray结构
    • VM结构
  • 函数以及参数说明
    • reallocate()
    • OP_CONSTANT

文件说明

文件名 功能
common 通用引用库文件
memory 数组增删的逻辑,基础的内存管理
chunk chunk节点:结构定义,属性的更改;储存源代码
value 常量池,常量类型结构的定义及实现
debug 反汇编提示信息
main 主函数
vm 虚拟机-指令执行器;解释执行源代码
compiler 编译
scanner 扫描代码段

流程

clox编译器整个代码逻辑流程

clox整个流程

执行减法的流程

计算器(执行操作码)

结构

chunk结构

1
2
3
4
5
6
7
typedef struct {
int count; //实际使用大小
int capacity; //分配数组的容量
uint8_t* code; //指令
int* lines; //线信息
ValueArray constants; //常量列表
} Chunk;

chunk
chunk扩展容量过程

  1. 分配具有更多容量的新数组
  2. 将现有元素从旧数组复制到新数组
  3. 更新capacity
  4. 删除旧数组
  5. 更新code指向新数组
  6. 既有空间,也可将元素储存在新数组中
  7. 更新cout

ValueArray结构

1
2
3
4
5
typedef struct{
int capacity; //容量
int count; //实际使用大小
Value* values; //数值
} ValueArray;

VM结构

1
2
3
4
typedef struct {
Chunk* chunk;
uint8_t* ip; //指令指针(PC机中的程序计数器)
} VM;

函数以及参数说明

reallocate函数

传递两个参数给reallocate()控制要实现的操作

oldsize newSize 操作
0 非0  分配新块
非0 0 重置为空
非0 < oldSize 缩小现有分配
非0 > oldSize 增加现有分配

OP_CONSTANT

OP_CONSTANT采用单字节操作数制定从块的常量数组加载哪个常量
format

  • 文章顶部

python核心(一)

发表于 2019-01-19 | 阅读次数

大海与生活

发表于 2018-09-22 | 分类于 随想 | 阅读次数
「密码不能告诉你」
阅读全文 »

电影

发表于 2018-09-22 | 分类于 随想 | 阅读次数
「喜欢的电影,人物」
阅读全文 »
12…6
pinocchio

pinocchio

喜欢探索,热爱编程

59 日志
18 分类
42 标签
RSS
Github E-mail
© 2017 - 2020 pinocchio
本站访客数:
博客全站共38.9k字