580-688-1371

1. 编译opencv android sdk
下面涉及到从android官网下载sdk command line tool的package,然后用sdkmanager来下载cmake,配置cmake
我碰到sdkmanager不能运行,发现时javac版本太旧了。设置JAVA_HOME指向1.8版本即可。
683 mkdir android-sdk
684 cd android-sdk
685 unzip ../sdk-tools-linux-4333796.zip
686 ls
687 tools/bin/sdkmanager –list
688 whch javac
689 which javac
690 ls -l /usr/bin/javac
691 ls -l /etc/alternatives/javac
692 ls /usr/lib/jvm/java-8-openjdk-amd64
693 export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64
694 tools/bin/sdkmanager –list
695 tools/bin/sdkmanager “cmake;3.10.2.4988404”
696 tools/bin/sdkmanager “build-tools;28.0.0”
697 tools/bin/sdkmanager “platforms;android-28”
698 ls
699 pwd
700 export ANDROID_SDK=/home/WD_4T/david/misc/opencv/android-sdk
701 cd cmake/
702 ls
703 cd 3.10.2.4988404/bin/
704 pwd
705 export PATH=/home/WD_4T/david/misc/opencv/android-sdk/cmake/3.10.2.4988404/bin:$PATH
706 cd ../..
707 cd ..
708 ls
709 cd ..
710 ls
711 cd android-ndk-r18b/
712 pwd
713 export ANDROID_NDK=/home/WD_4T/david/misc/opencv/android-ndk-r18b
714 cd ..
715 mkdir test2
716 cd test2
717 ls
719 python ../opencv/platforms/android/build_sdk.py –extra_modules_path ../opencv_contrib/modules/ . ../opencv
720 ls
721 cd OpenCV-android-sdk/
722 ls
723 cd sdk/
724 ls
725 cd ../..
726 ls
727 ../android-sdk/tools/bin/sdkmanager –list
728 ../android-sdk/tools/bin/sdkmanager “ndk-bundle”
729 python ../opencv/platforms/android/build_sdk.py –extra_modules_path ../opencv_contrib/modules/ . ../opencv

2. 在Linux上调试opencv
我的oepncv和opencv_contrib放在一个目录里,并且在这个目录里还创建了一个build目录,用来存放编译结果

cd build
cmake -D CMAKE_BUILD_TYPE=Release -DOPENCV_EXTRA_MODULES_PATH=../opencv_contrib -D CMAKE_INSTALL_PREFIX=/usr/local ..

3. 编译demo
比方说我们基于opencv写了一个demo,怎么编译呢?比方说把/github.com/opencv/opencv_contrib/blob/master/modules/face/samples/facerec_lbph.cpp拷贝出来,我想把它编译为一个可执行文件

先创建CMakeLists.txt,包含如下内容

cmake_minimum_required(VERSION 2.8)
set (CMAKE_CXX_STANDARD 11)
project( test )
find_package( OpenCV REQUIRED PATHS ~/misc/opencv/build )
include_directories( ${OpenCV_INCLUDE_DIRS} )
add_executable( test facerec_lbph.cpp )
target_link_libraries( test ${OpenCV_LIBS} )

然后运行
cmake -H. -Bbin
cmake –build bin

在bin/test就是生成的可执行文件。实际测试发现,用LBPH识别att_faces,错误率达到117/360,用lfw数据集更差。

4. dlib
/dlib.net/faq.html#Whyisdlibslow

5. 更多信息
/www.shervinemami.info/faceRecognition.html
这个页面详细介绍了用opencv eigenface来进行人脸识别,并给出了提高准确率的建议。文中提到人脸检测有90%-95%的成功率,但人脸识别只有30%-70%成功率。

/github.com/davisking/dlib/blob/master/examples/dnn_face_recognition_ex.cpp
Davis King的dlib,用神经网络进行人脸识别

蓝牙传输apk失败

今天用小米手机发送apk到Nexus 6p,试了几次均失败。什么原因呢?

查看了一下小米手机日志,说对方拒绝了

01-11 15:43:53.883  8796 21043 D BtOppObexClient: OBEX session created
01-11 15:43:53.895  8796 21043 D BluetoothOpp: sendIntentIfCompleted :192 URI :content:/com.android.bluetooth.opp/btopp/20
01-11 15:43:53.956  8796 21043 I BtOppObexClient: Remote reject, Response code is 207
01-11 15:43:53.956  8796 21043 I BtOppObexClient: Remote reject file type application/vnd.android.package-archive
01-11 15:43:53.957  8796 21043 D BluetoothOppUtility: closeSendFileInfo: uri=content:/com.android.fileexplorer.myprovider/external_files/Download/TmallGenie-release_3.8.1_1545621634.apk@f69cdce
01-11 15:43:53.957  8796 21043 V BtOppObexClient: Get response code 207

再查看Nexus 6p的日志,提示“mimeType is null or in unacceptable list, reject the transfer”

01-11 07:46:41.997   992  1406 D ObexServerSockets0: Accepted socket connection from: ServerSocket: Type: TYPE_L2CAP Channel: 4097
01-11 07:46:41.997   992  1406 D ObexServerSockets0: onConnect() socket: android.bluetooth.BluetoothSocket@6c8ef94 mConAccepted = false
01-11 07:46:41.997   992  1406 D BtOppService:  onConnect BluetoothSocket :android.bluetooth.BluetoothSocket@6c8ef94 
01-11 07:46:41.997   992  1406 D BtOppService:  :device :E4:46:DA:DC:8E:34
01-11 07:46:41.997   992  1406 D ObexServerSockets0: Accepting socket connection...
01-11 07:46:41.997   992   992 D BtOppService: Get incoming connection
01-11 07:46:41.997   992   992 I BtOppService: Start Obex Server
01-11 07:46:41.998   992   992 D BtOppObexServer: Create ServerSession with transport com.android.bluetooth.BluetoothObexTransport@d054a3d
01-11 07:46:41.998   992   992 D BtOppService: Get ServerSession com.android.bluetooth.opp.BluetoothOppObexServerSession@d6232 for incoming connectioncom.android.bluetooth.BluetoothObexTransport@d054a3d
01-11 07:46:42.031   992  1139 D bt_btif_config: btif_get_device_type: Device [e4:46:da:dc:8e:34] type 1
01-11 07:46:42.031   992  1139 I bt_btif_dm: get_cod remote_cod = 0x005a020c
01-11 07:46:42.032   992  1139 I BluetoothBondStateMachine: bondStateChangeCallback: Status: 0 Address: E4:46:DA:DC:8E:34 newState: 0
01-11 07:46:42.032   992  1247 D BluetoothAdapterProperties: Failed to remove device: E4:46:DA:DC:8E:34
01-11 07:46:42.035   992  1247 I BluetoothBondStateMachine: Bond State Change Intent:E4:46:DA:DC:8E:34 OldState: 11 NewState: 10
01-11 07:46:42.036   992  1247 D HidService: getHidService(): returning com.android.bluetooth.hid.HidService@4042fa3
01-11 07:46:42.037   992  1247 D A2dpSinkService: getA2dpSinkService(): service is NULL
01-11 07:46:42.039   992  1247 D HidService: Saved priority E4:46:DA:DC:8E:34 = -1
01-11 07:46:42.042   992  1247 I BluetoothBondStateMachine: StableState(): Entering Off State
01-11 07:46:42.046  1191  1191 W BluetoothEventManager: showUnbondMessage: Not displaying any message for reason: 0
01-11 07:46:42.049  1025  1161 W BluetoothEventManager: showUnbondMessage: Not displaying any message for reason: 0
01-11 07:46:42.144   992  4779 W Obex ServerSession: Requested MaxObexPacketSize 8087 is larger than the max size supported by the transport: 8085 Reducing to this size.
01-11 07:46:42.145   992  4779 D BtOppObexServer: onConnect
01-11 07:46:42.480   655   655 W Atfwd_Sendcmd: AtCmdFwd service not published, waiting... retryCnt : 4
01-11 07:46:43.194   992  4779 D BtOppObexServer: onPut javax.obex.ServerOperation@e4a3b83
01-11 07:46:43.207   992  4779 W BtOppObexServer: mimeType is null or in unacceptable list, reject the transfer / !!!!!!!!!!!!
01-11 07:46:43.442   992  4779 D BtOppObexServer: onDisconnect

在Android代码例搜索了一下,找到了相应的代码

                                                
            / Bluetooth/src/com/android/bluetooth/opp/BluetoothOppObexServerSession.java

            / Reject policy: anything outside the "white list" plus unspecified
            / MIME Types. Also reject everything in the "black list".
            if (!pre_reject
                    && (mimeType == null                                                                                          
                            || (!isWhitelisted && !Constants.mimeTypeMatches(mimeType,
                                    Constants.ACCEPTABLE_SHARE_INBOUND_TYPES))
                            || Constants.mimeTypeMatches(mimeType,
                                    Constants.UNACCEPTABLE_SHARE_INBOUND_TYPES))) {
                if (D) Log.w(TAG, "mimeType is null or in unacceptable list, reject the transfer");
                pre_reject = true;
                obexResponse = ResponseCodes.OBEX_HTTP_UNSUPPORTED_TYPE;
            }

apk不在支持的类型内

    Bluetooth/src/com/android/bluetooth/opp/Constants.java
    public static final String[] ACCEPTABLE_SHARE_INBOUND_TYPES = new String[] {
        "image/*",
        "video/*",
        "audio/*",
        "text/x-vcard",
        "text/x-vcalendar",
        "text/calendar",
        "text/plain",
        "text/html",
        "text/xml",
        "application/zip",
        "application/vnd.ms-excel",
        "application/msword",
        "application/vnd.ms-powerpoint",
        "application/pdf",
        "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
        "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
        "application/vnd.openxmlformats-officedocument.presentationml.presentation",
        "application/x-hwp",
    };

将.apk修改为.zip,就可以用蓝牙传输了。

Machine Learning学习笔记

Naive Bayes Classification朴素贝叶斯分类器

Gaussian Naive Bayes
Multinomial Naive Bayes
用TF-IDF vectorizer和MultinomialNB组成pipeline对文本进行分类

Linear Regression线性回归

Simple Linear Regression,由给定的点集计算斜率和截距,以及多维线性函数的因子

rng = np.random.RandomState(1)
X = 10 * rng.rand(100, 3)
y = 0.5 + np.dot(X, [1.5, -2., 1.])
model.fit(X, y)
print(model.intercept_)
print(model.coef_)

Basis Function Regression基函数回归
Polyomial Basis Function
多项式回归处理有噪音的sin波形

from sklearn.pipeline import make_pipeline
poly_model = make_pipeline(PolynomialFeatures(7),
LinearRegression())

rng = np.random.RandomState(1)
x = 10 * rng.rand(50)
y = np.sin(x) + 0.1 * rng.randn(50)
poly_model.fit(x[:, np.newaxis], y)
yfit = poly_model.predict(xfit[:, np.newaxis])
plt.scatter(x, y)
plt.plot(xfit, yfit);

7次多项式可以很好的拟合数据

Gaussian Basis Function
Regularization:Ridge Regression岭回归和Lasso Regularization

Support Vector Machine支持向量机

画直线来分割分类时,如果用0宽度的直线,可能导致有几条符合要求的直线。这样就引入了SVM:用最大宽度的一条直线来分割,直线上的点称为支持向量。
SVC(Support Vector Classifier)可以指定kernel为linear或rbf。如果数据重合,需要指定C参数软化边界(C越大边界越硬)
本节的例子是将RadomizedPCA和SVC(kernel=‘rbf’)串起来进行人脸识别

Decision Trees and Random Forests决策树和随机森林

决策树分类器DecisionTreeClassifier
将DecisionTreeClassifier和BaggingClassifier组合在一起使用可以处理重合数据;RandomForestClassifier拥有一样的功能,使用更方便
随机森林也可以用于回归(Regression),处理连续数据

两个sin波形叠加
rng = np.random.RandomState(42)
x = 10 * rng.rand(200)

def model(x, sigma=0.3):
    fast_oscillation = np.sin(5 * x)
    slow_oscillation = np.sin(0.5 * x)
    noise = sigma * rng.randn(len(x))
    return slow_oscillation + fast_oscillation + noise

y = model(x)
plt.errorbar(x, y, 0.3, fmt='o');

画出真实的ytrue和预测的yfit
from sklearn.ensemble import RandomForestRegressor
forest = RandomForestRegressor(200) forest.fit(x[:, None], y)
xfit = np.linspace(0, 10, 1000)
yfit = forest.predict(xfit[:, None])
ytrue = model(xfit, sigma=0)
plt.errorbar(x, y, 0.3, fmt='o', alpha=0.5)
plt.plot(xfit, yfit, '-r');
plt.plot(xfit, ytrue, '-k', alpha=0.5);

本节例子用RandomForestClassifier来识别手写数字

Principal Component Analysis主成分分析

PCA是无监督算法。是一种降维算法,保留贡献最大方差的维度。
手写数字识别示例中,将8×8(即64维)的图像降低到二维(PCA1和PCA2),然后以颜色作为数字种类,画图。
PCA作为噪音过滤器:大方差的成分不易受噪音干扰。示例:在手写数字中加入噪音,指定PCA保留50%的方差,可以计算出PCA数目。通过transform/inverse_transform可以计算得到去除噪音的数据

def plot_digits(data):
 
fig, axes = plt.subplots(4, 10, figsize=(10, 4),
    subplot_kw={'xticks':[], 'yticks':[]},
    gridspec_kw=dict(hspace=0.1, wspace=0.1))
for i, ax in enumerate(axes.flat):
    ax.imshow(data[i].reshape(8, 8),
    cmap='binary', interpolation='nearest',
    clim=(0, 16))
plot_digits(digits.data)

np.random.seed(42)
noisy = np.random.normal(digits.data, 4)
plot_digits(noisy)

pca = PCA(0.50).fit(noisy)
pca.n_components_

components = pca.transform(noisy)
filtered = pca.inverse_transform(components)
plot_digits(filtered)

本节的例子是从头像中提取特征脸(Eigenface),从62×47降维到150,然后重构头像

Manifold Learning流体学习

在数据间有非线性关系时PCA不能很好工作,这时用到流体学习。
流体学习方法:multidimensional scaling (MDS), locally linear embedding (LLE), and isometric mapping (Isomap) ,t-distributed stochastic neighbor embedding (t- SNE)
各个点的数据不是关键,关键的是彼此的距离。
MDS:所有点的距离计入考量。LLE:相邻点的距离才计入考量。所以hello卷成S形时,MDS无法工作,但LLE可以。
本节的例子:用Isomap分析名人头像得出明暗程度和脸的朝向;分析手写体所有的“1”的轮廓特征。它不能帮我们归类,但可以帮助分析数据,指导如何处理数据。

k-Means Clustering

k-Means基于两个概念:1)Cluster中心是该Cluster的算术均值;2)每个点到本Cluster的距离比到其它Cluster的距离要近
k-Means示例:给定几个Cluster的点集,计算得出Cluster的中心
当Cluster有复杂的几何形状时,k-Means会失效。可以换用SpectralClustering评估器,它将相邻点的关系计入考量。
本节的例子:将手写数字图片(包含0-9)分成10个Cluster。仅使用KMeans得到79%的识别率,在使用KMeans前用t-distributed stochastic neighbor embedding (t-SNE)
处理数据则可以得到93%的识别率。
另一个例子:使用MiniBatchKMeans进行图片颜色压缩,从真彩色(2^24种颜色)缩减到16中颜色。每个点的颜色设置为它的Cluster中心的颜色。

Gaussian Mixture Models高斯混合模型

k-Means的缺陷:基于距离而不是概率,导致现实使用中出现问题。比如两个点集有重合时,难以判断边界点的归属。另外,如果Cluster的分布不呈现圆形时,也不能很好的工作。需要基于概率以及需要更灵活的形状,就导致出现GMM。

covariance type有diag(椭圆,与坐标轴平行),spherical(圆形),full(任意方向的椭圆)

GMM评估月牙行的数据时,是几个component的叠加。

本节的例子:GMM分析手写数字,并生成新数字。

Kernel Density Estimation核密度估计

histogram(直方图)用作密度评估的问题时,如果选择的bin颗粒度太大或者跟数据没对齐,会导致相同的数据有不同的密度函数。使用KDE可以解决这个问题。

两种kernel类型:tophat(将密度与点位置对齐而不是与bin对齐)和Gaussian(在tophat基础上加入Gaussian平滑函数)。

例1:用密度图画出南美两种动物的分布。

例2:在朴素贝叶斯那一节,GaussianNB用的是与坐标轴对齐的模型。本例用的是更高级的模型。用该模型识别手写数字可以达到96.6%,但GaussianNB只能达到81.8%。该模型的思路是

1. Split the training data by label.
2. For each set, fit a KDE to obtain a generative model of the data. This allows you for any observation x and label y to compute a likelihood P(x|y).
3. From the number of examples of each class in the training set, compute the class prior, P(y).
4. For an unknown point x, the posterior probability for each class is P(y|x)∝P(x|y)P(y). The class that maximizes this posterior is the label assigned to the point.

 

adb tcp

前不久在调试一个巨大的apk(大约150M字节)时碰到问题,这个设备usb口不够用,无法用adb usb连接。我启动adb tcp,发现安装apk慢的出奇,实在无法忍受。在Nexus 6p(Android M)上试了一下,也有很慢的问题。我的Wi-Fi连接是没有问题的,用iperf测速有300M以上,所以瓶颈不在Wi-Fi。一开始怀疑adb tcp用的buffer不够,调整后adb tcp速度仍然无法改善。

偶然发现一个链接,讲到Android N在adb的速度上有改善,于是将Nexus 6p升级到了Android O。果然,速度一下上来了。以下是M和O在不同场景下的速度对比

M O
iperf3测速 390 Mbits/sec 363 Mbits/sec
iperf3测速(写文件到/data/1.dat) 138 Mbits/sec 164 Mbits/sec
adb push文件到/data/,usb 3.4 MB/s 17.8 MB/s
adb push文件到/data/,tcp 0.7 MB/s 16.6 MB/s

附:为了调试方便,我希望Android的adbd进程默认也开启tcp,这样只要有网络连接时可以随意切换到adb tcp。方法是setprop service.adb.tcp.port 5555。adbd在启动时会检查该property,如果设置了就启动tcp监听。

(system/core/adb/daemon/main.cpp)

    / If one of these properties is set, also listen on that port.
    / If one of the properties isn't set and we couldn't listen on usb, listen
    / on the default port.
    std::string prop_port = android::base::GetProperty("service.adb.tcp.port", "");
    if (prop_port.empty()) {
        prop_port = android::base::GetProperty("persist.adb.tcp.port", "");                                                       
    }

    int port;
    if (sscanf(prop_port.c_str(), "%d", &port) == 1 && port > 0) {
        D("using port=%d", port);
        / Listen on TCP port specified by service.adb.tcp.port property.
        setup_port(port);
    } else if (!is_usb) {
        / Listen on default port.
        setup_port(DEFAULT_ADB_LOCAL_TRANSPORT_PORT);
    }

另外的一个好处是,persist.adb.tcp.port会作为persistent property保存在/data/property/persist.adb.tcp.port文件中,这样系统重启后也会启动adb tcp。

815-205-2200

偶然发现在Nexus 5上join multicast group时看不到igmp报文出来,这样路由器能知道mcast group与mac的对应关系来转发组播报文吗?

MulticastSocket s = new MulticastSocket();
InetAddress addr = InetAddress.getByName("230.230.230.1");
s.joinGroup(addr);

我在Ubuntu上试了一下同样的代码,的确能看到报文。在Nexus 6p上也能看到。

95	8.113230	192.168.43.1	224.0.0.22	IGMPv3	54		Membership Report / Join group 239.246.1.1 for any sources

Nexus 5与它们差在哪里呢?原来Nexus 5的kernel不支持igmp reporting。这个功能是由CONFIG_IP_MULTICAST控制的。如果定义了这个宏,kernel支持igmp reporting,且会创建/proc/net/igmp。如果没有定义这两个宏,kernel依然支持multicast,只是不支持reporting。

/proc/net/igmp在igmp_net_init()中(net/ipv4/igmp.c)被创建:

static int __net_init igmp_net_init(struct net *net)
{
    struct proc_dir_entry *pde;
    pde = proc_net_fops_create(net, "igmp", S_IRUGO, &igmp_mc_seq_fops);

果然,Ubuntu和Nexus 6p的确有这个文件。我们可以在加入mcast group前后来看看/proc/net/igmp内容的变化:(用iperf -s -u -B 239.246.1.1来加入group)

angler:/ # cat /proc/net/igmp
Idx	Device    : Count Querier	Group    Users Timer	Reporter
1	lo        :     1      V3
				010000E0     1 0:00000000		0
2	dummy0    :     1      V3
				010000E0     1 0:00000000		0
4	rmnet_ipa0:     1      V3
				010000E0     1 0:00000000		0
5	wlan0     :     2      V2
				FB0000E0     1 0:00000000		1
				010000E0     1 0:00000000		0
angler:/ # cat /proc/net/igmp                                                                                                    
Idx	Device    : Count Querier	Group    Users Timer	Reporter
1	lo        :     1      V3
				010000E0     1 0:00000000		0
2	dummy0    :     1      V3
				010000E0     1 0:00000000		0
4	rmnet_ipa0:     1      V3
				010000E0     1 0:00000000		0
5	wlan0     :     3      V2
				0101F6EF     1 1:00000089		1
				FB0000E0     1 0:00000000		1
				010000E0     1 0:00000000		0

可以看到,kernel对wlan0接口添加了一个条目,0101F6EF刚好是239.246.1.1

337-858-1922

数组的初始化

np.zeros(10, dtype=int) # Create a length-10 integer array filled with zeros
np.ones((3, 5), dtype=float) # Create a 3×5 floating-point array filled with 1s
np.full((3, 5), 3.14) # Create a 3×5 array filled with 3.14
np.arange(10) #array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
np.arange(0, 20, 2) # from 0 to 20, stepping 2. array([ 0, 2, 4, 6, 8, 10, 12, 14, 16, 18])
np.linspace(0, 1, 5) # array([ 0. , 0.25, 0.5 , 0.75, 1. ])
np.random.randint(0, 10, (3, 3)) # Create a 3×3 array of random integers in the interval [0, 10)
np.empty(3) # Create an uninitialized array of three integers

通用函数

循环方式操作一个数组速度非常慢,可以用通用函数解决。

例子:求倒数

import numpy as np
np.random.seed(0)
def compute_reciprocals(values):
    output = np.empty(len(values))
    for i in range(len(values)):
        output[i] = 1.0 / values[i]
    return output
values = np.random.randint(1, 10, size=5)
compute_reciprocals(values)

改用UFuncs方式求倒数
print(1.0 / values)

在两个数组上作运算
In[5]: np.arange(5) / np.arange(1, 6)
Out[5]: array([ 0. , 0.5 , 0.66666667, 0.75 , 0.8 ])

In[6]: x = np.arange(9).reshape((3, 3)) 2**x
Out[6]: array([[ 1, 2, 4],
[ 8, 16, 32],
[ 64, 128, 256]])

数组运算

In[7]: x = np.arange(4)
print(“x =”, x)
print(“x + 5 =”, x + 5)
print(“x – 5 =”, x – 5)
print(“x * 2 =”, x * 2)
print(“x / 2 =”, x / 2)
print(“x / 2 =”, x / 2) # floor division
print(“-x = “, -x) print(“x ** 2 = “, x ** 2)
print(“x % 2 = “, x % 2)

x = [0 1 2 3]
x + 5 = [5 6 7 8]
x – 5 = [-5 -4 -3 -2]
x * 2 = [0 2 4 6]
x /2 = [ 0. 0.5 1. 1.5]
x / 2 = [0 0 1 1]
-x = [ 0 -1 -2 -3]
x ** 2 = [0 1 4 9]
x% 2 = [0 1 0 1]

运算可以聚合在一块:
In[9]: -(0.5*x + 1) ** 2
Out[9]: array([-1. , -2.25, -4. , -6.25])

这些运算符实际是NumPy函数的wrapper。可以直接调用NumPy函数
In[10]: np.add(x, 2)
Out[10]: array([2, 3, 4, 5])

取绝对值
In[11]: x = np.array([-2, -1, 0, 1, 2])
abs(x)
Out[11]: array([2, 1, 0, 1, 2])

复数运算
In[14]:x=np.array([3-4j,4-3j,2+0j,0+1j]) np.abs(x)
Out[14]: array([ 5., 5., 2., 1.])

三角函数
In[15]: theta = np.linspace(0, np.pi, 3)
In[16]: print(“theta = “, theta)
print(“sin(theta) = “, np.sin(theta))
print(“cos(theta) = “, np.cos(theta))
print(“tan(theta) = “, np.tan(theta))
theta = [ 0. 1.57079633 3.14159265]
sin(theta) = [ 0.00000000e+00 1.00000000e+00 1.22464680e-16]
cos(theta) = [ 1.00000000e+00 6.12323400e-17 -1.00000000e+00]
tan(theta) = [ 0.00000000e+00 1.63312394e+16 -1.22464680e-16]

反三角函数
In[17]: x = [-1, 0, 1]
print(“x = “, x)
print(“arcsin(x) = “, np.arcsin(x))
print(“arccos(x) = “, np.arccos(x))
print(“arctan(x) = “, np.arctan(x))
x = [-1, 0, 1]
arcsin(x) = [-1.57079633 0. 1.57079633]
arccos(x) = [ 3.14159265 1.57079633 0. ]
arctan(x) = [-0.78539816 0. 0.78539816]

指数和对数
In[18]: x = [1, 2, 3]
print(“x =”, x)
print(“e^x =”,np.exp(x))
print(“2^x =”,np.exp2(x))
print(“3^x =”,np.power(3,x))
x = [1, 2, 3]
e^x = [ 2.71828183 7.3890561 20.08553692]
2^x = [ 2. 4. 8.]
3^x = [ 3 9 27]

In[19]: x = [1, 2, 4, 10]
print(“x =”, x)
print(“ln(x) =”, np.log(x))
print(“log2(x) =”, np.log2(x))
print(“log10(x) =”, np.log10(x))

通过out参数直接将结果存入指定位置
In[24]: x = np.arange(5)
y = np.empty(5)
np.multiply(x, 10, out=y)
print(y)
[ 0. 10. 20. 30. 40.]

如果用y = np.arange(5)*10,将创建临时数组,然后将临时数组的内容拷贝到y

隔个存放:
In[25]: y = np.zeros(10)
np.power(2, x, out=y[::2])
print(y)
[ 1. 0. 2. 0. 4. 0. 8. 0. 16. 0.]

聚合
reduce重复作用于一个数组,直到只剩一个元素
In[26]: x = np.arange(1, 6)
np.add.reduce(x)
Out[26]: 15

In[27]: np.multiply.reduce(x)
Out[27]: 120

获得中间结果的数组
In[28]: np.add.accumulate(x)
Out[28]: array([ 1, 3, 6, 10, 15])
In[29]: np.multiply.accumulate(x)
Out[29]: array([ 1, 2, 6, 24, 120])

外积(Outer product)
In[30]: x = np.arange(1, 6)
np.multiply.outer(x, x)
Out[30]: array([[ 1, 2, 3, 4, 5],
[ 2, 4, 6, 8, 10],
[ 3, 6, 9, 12, 15],
[ 4, 8, 12, 16, 20],
[ 5, 10, 15, 20, 25]])

广播

广播允许二院运算符作用于不同大小的数组上
In[2]: a = np.array([0, 1, 2])
In[3]:a+5
Out[3]: array([5, 6, 7])

我们可以想象5被扩充为[5, 5, 5],然后运算。实际上并未发生,只是这样可以帮助我们理解。

广播的规则:
1. 如果两个数组维数不一样,维数少的在左边加维
2. 如果两个数组的维数不匹配,维数为1的进行扩展,以匹配对应的维数
3. 如果两个数组的维数不匹配,且维数都不为1,则出错

例1
In[8]: M = np.ones((2, 3))
a = np.arange(3)

In[9]:M+a
Out[9]: array([[ 1., 2., 3.],
[ 1., 2., 3.]])

按照规则1,a扩展维数变为(1)(3)
按照规则2,a扩展为 (3)(3),即三行都是[0, 1, 2]
维数匹配,可以进行运算

例2
In[10]: a = np.arange(3).reshape((3, 1))
b = np.arange(3)
In[11]: a + b
Out[11]: array([[0, 1, 2],
[1, 2, 3],
[2, 3, 4]])

按照规则1,b扩展维数变为(1)(3)
按照规则2,a和b都扩展,变为(3)(3)

例3:
In[12]: M = np.ones((3, 2))
a = np.arange(3)

按照规则1,a扩展变为(1)(3)
按照规则2,a变为(3)(3)
此时维数依然不匹配,且维数都不为1,按照规则3,运算会出错

如果想要运算成功,可以M + a[:, np.newaxis],a[:, np.newaxis]将行数组变成了列数组。也可以用M+a.reshape(3, 1)

运用广播来绘图
# x and y have 50 steps from 0 to 5
x = np.linspace(0, 5, 50)
y = np.linspace(0, 5, 50)[:, np.newaxis]
z = np.sin(x) ** 10 + np.cos(10 + y * x) * np.cos(x)
import matplotlib.pyplot as plt
In[23]: plt.imshow(z, origin=’lower’, extent=[0, 5, 0, 5],
cmap=’viridis’)
plt.colorbar();

# np.dot计算两个向量的点积
In [27]: np.dot([1, 2, 3], [2, 2, 2])
Out[27]: 12

In [28]: np.dot(([1, 2, 3], [4, 5, 6]), [2, 2, 2])
Out[28]: array([12, 30])

# rng.rand得出[0, 1]区间的正则分布数据,参数指定维数
In [30]: rng = np.random.RandomState(1)

In [31]: rng.rand(4)
Out[31]: array([4.17022005e-01, 7.20324493e-01, 1.14374817e-04, 3.02332573e-01])

In [32]: rng.rand(4, 2)
Out[32]:
array([[0.14675589, 0.09233859],
[0.18626021, 0.34556073],
[0.39676747, 0.53881673],
[0.41919451, 0.6852195 ]])

yuv图像格式

Android默认的preview格式是nv21,它是yuv420sp(semi-planar)格式。这种格式每个像素点有一个y分量,每2×2个像素点(即4个,横纵各两个)公用一个u分量和一个v分量。所以,每个像素占用1.5Byte(12bit)。

给定长w宽h的帧,nv21占用w*h*3/2字节。开始的w*h字节依次存放y分量,接着的w*h/2字节交织存放v和u分量。

另外还有一种yuv420sp格式,即nv12。它和nv21相同,只是u和v的存放调过来了,u和v分量交织存放。

xargs和find-exec

一般我比较喜欢用xargs,因为它符合unix的风格。比如我想找到所有的apk文件,并查看apk的大小信息:

find . -name “*.apk” | xargs ls -l

xargs从管道中读取输入,把每行追加到ls -l后,执行。但有些任务xargs办不到,比如我想把这些apk拷贝到另一个位置,因为apk文件名不是位于命令末尾。这时就需要用find exec了。

find . -name “*.apk” -exec cp -v {} /sdcard/new \;

这样就将找到的apk都copy到/sdcard/new。find会用apk文件名替换{},然后运行cp。find exec要求命令用”;”或者”+”结束,但”;”需要转义,所以变成了”\;”

另一个结束符”+”,表示将find的结果尽可能多地替换到{}。stackoverflow上有一个解释:
find will execute grep and will substitute {} with the filename(s) found. The difference between ; and + is that with ; a single grep command for each file is executed whereas with + as many files as possible are given as parameters to grep at once.

比如

$ find . -name "k*.txt" -exec wc {} +
      66     846    6880 ./k2.txt
    4547   32809  245241 ./kernel1.txt
    9423  104649 1019945 ./k3.txt
   10571  101032 1016558 ./reinit/k2.txt
    9173   97774 1020006 ./k4.txt
     380    9172   52084 ./k.txt
   34160  346282 3360714 total

$ find . -name "k*.txt" -exec wc {} \;
      66     846    6880 ./k2.txt
    4547   32809  245241 ./kernel1.txt
    9423  104649 1019945 ./k3.txt
   10571  101032 1016558 ./reinit/k2.txt
    9173   97774 1020006 ./k4.txt
     380    9172   52084 ./k.txt

一个运行的是一个命令wc ./k2.txt ./kernel1.txt …,另一个则运行几个命令
wc ./k2.txt
wc ./kernel1.txt等等

四次握手和组播握手

四次握手

PTK = PRF-Length(PMK, “Pairwise key expansion”, Min(AA,SPA) || Max(AA,SPA) || Min(ANonce,SNonce) || Max(ANonce,SNonce))

Length=KCK_bits+KEK_bits+TK_bits
Length为三者之和。前面两个长度跟AKM suite有关,不同的suite有不同的长度值(一般为128)。TK_bits跟Cipher suite有关。

KCK= L(PTK, 0, KCK_bits)
KEK = L(PTK, KCK_bits, KEK_bits)
TK = L(PTK, KCK_bits+KEK_bits, TK_bits)

算得PTK后,KCK,KEK和TK按照各自的长度在PTK中一次取特定长度的内容。KCK用来计算MIC使用,KEK用来加密EAPOL报文中的Key data。TK用来设置到MAC中,进行数据加密。

PMKID = Truncate-128(HMAC-SHA-256(PMK, “PMK Name” || AA || SPA))

PMKID通过以上公式算得,不同的AKM使用不同长度的HMAC-SHA,有256,384等等。

组播握手

AP在特定时机随机产生GMK,由GMK生成GTK。GTK用来保护组播数据。

GTK = PRF-Length(GMK, “Group key expansion”, AA || GNonce) 
TK = L(GTK, 0, TK_bits)

EAPOL帧的表示

EAPOL-Key(S, M, A, I, K, SM, KeyRSC, ANonce/SNonce, MIC, DataKDs)

S:means the initial key exchange is complete
M:means the MIC is available in message
A:means a response is required to this message
I:is the Install bit: Install/Not install for the pairwise key
K:is the key type: P (Pairwise), G (Group/SMK)
SM:is the SMK Message bit
KeyRSC:is the key RSC. This is the Key RSC field

下面代码截取自wpa_supplicant源代码wpa_supplicant_8/src/rsn_supp/wpa.c

wpa_sm_rx_eapol() {
    /如果设置了M,则用ptk中的kck检验mic
    if ((key_info & WPA_KEY_INFO_MIC) && !peerkey &&
        wpa_supplicant_verify_eapol_key_mic(sm, key192, ver, tmp, data_len))
        goto out;

    /如果设置了KeyRSC,则用kek解密key data
    if ((sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) &&
        (key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
        if (wpa_supplicant_decrypt_key_data(sm, key, ver, key_data,
                            &key_data_len))
            goto out;
    }

    if (key_info & WPA_KEY_INFO_KEY_TYPE) {
        if (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) {
            wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
                "WPA: Ignored EAPOL-Key (Pairwise) with "
                "non-zero key index");
            goto out;
        }            
        /peerkey仅作用于802.11e DLS                                                                                                             
        if (peerkey) {
            /* PeerKey 4-Way Handshake */
            peerkey_rx_eapol_4way(sm, peerkey, key, key_info, ver,
                          key_data, key_data_len);
        } else if (key_info & WPA_KEY_INFO_MIC) {/对于supplicant来说,设置了M就是3/4,否则就是1/4消息
            /* 3/4 4-Way Handshake */
            wpa_supplicant_process_3_of_4(sm, key, ver, key_data,
                              key_data_len);
        } else {
            /* 1/4 4-Way Handshake */
            wpa_supplicant_process_1_of_4(sm, src_addr, key,
                              ver, key_data,
                              key_data_len);
        }
    } else if (key_info & WPA_KEY_INFO_SMK_MESSAGE) {
        /* PeerKey SMK Handshake */
        peerkey_rx_eapol_smk(sm, src_addr, key, key_data_len, key_info,
                     ver);
    } else {
        if (key_info & WPA_KEY_INFO_MIC) {
            /* 1/2 Group Key Handshake */
            wpa_supplicant_process_1_of_2(sm, src_addr, key,
                              key_data, key_data_len,
                              ver);
        } else {
            wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
                "WPA: EAPOL-Key (Group) without Mic bit - "
                "dropped");
        }
    }
}

可以看到,收到eapol消息后,如果包含key_info设置了M(包含MIC)的话,就用KCK来验证MIC。如果设置了KeyRSC的话,就用KEK来解密Key Data。另外,在收到1/4消息会计算ptk,但不安装。在收到3/4消息后,如果设置了I(Install),则安装TK。

wpa_supplicant_process_3_of_4() {
    if (sm->group_cipher == WPA_CIPHER_GTK_NOT_USED) {
        wpa_supplicant_key_neg_complete(sm, sm->bssid,
                        key_info & WPA_KEY_INFO_SECURE);
    } else if (ie.gtk &&
        wpa_supplicant_pairwise_gtk(sm, key,
                    ie.gtk, ie.gtk_len, key_info) < 0) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
            "RSN: Failed to configure GTK");
        goto failed;
    }

    if (ieee80211w_set_keys(sm, &ie) < 0) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
            "RSN: Failed to configure IGTK");
        goto failed;
    }
}

如果ie包含gtk的话,就安装gtk。如果支持mfp且ie包含igtk,则设置igtk到mac。

另外,wpa_supplicant代码中还看到有一个peerkey,只有在DLS时才用到

# PeerKey handshake for Station to Station Link (IEEE 802.11e DLS)
CONFIG_PEERKEY=y

一个示例的4次握手和组播握手抓包oleograph,用12345678:n5解密