0%

局域网代理网络

问题描述

目前具有一台能够正常通过wifi连接互联网的Windows PC,另外有一台不具有wifi功能的Linux主机。WIndows和Linux都具有一个空闲网卡。现在希望Linux通过Windows的网络访问互联网,并且两者能够处于同一个局域网下,互相传输文件也快一些。

处理步骤

  1. 将两台主机用网线连接。
  2. 在Windows上,打开网络适配器:

image-20230922125724577

可以看到有wifi适配器和ethernet适配器,ethernet是连接了网线的网卡。

  1. 打开wifi适配器的属性-共享,设置网络分享,分享到Ethernet:

image-20230922125930445

点击确定后再点击确认。

  1. 返回适配器页面,打开ethernet适配器的属性-ipv4:

image-20230922130118645

可以看到windows已经自动分配了此网卡的ip地址和掩码:

image-20230922130215852

记录此ip地址和网络掩码。

  1. 在linux主机上,配置网卡(此处为KDE的网络管理界面):

2023-09-22_13-05

配置如上图所示,注意Linux本身的IP和Windows的必须不同,网关设置为Windows的ethernet网卡的ip。

  1. Linux上点击连接网络,即可以连接到互联网。

我对latex的使用和了解也只是皮毛,这篇文章仅用于记录如何在文章的参考文献部分使用gbt7714的参考格式。因为部分的latex模板不提供该格式的支持(例如我的本科毕业论文模板)。

下载环境包

一般而言,普通的参考文献格式(例如IEEEtran)是可以在下载完TexLive后直接使用的,但是gbt7714并没有包含在环境当中。因此需要在GitHub上下载相关的包。下面是一个gbt7714环境的仓库:

gbt7714-bibtex-style

在release页面下载压缩包并解压后,选择其中的gbt7714.stygbt7714-numerial.bstgbt7714-author-year.bst文件复制到自己的文章tex文件所在的目录下。如果需要早期的2005版,则添加文件名内有2005的文件。但是2005的命令使用稍有不同,可以查看相关资料(我也没有用过)。

文章内使用gbt7714

在tex文件最开始的导言区添加两行命令:\usepackage{gbt7714}\bibliographystyle,效果如下:

1
2
3
4
5
6
\documentclass[bachelor, print]{xduthesis}
\usepackage{gbt7714}
\bibliographystyle{gbt7714-numerical}
\usepackage{graphicx}
\usepackage{subfig}
\usepackage{booktabs}

注意gbt7714-numerical的参考文献顺序是正常的文章内的文献引用顺序,gbt7714-author-year则是按照作者-年份的顺序进行排序,一般而言,使用的是gbt7714-numerical。至于添加参考文献bib文件的语句\bibliography{XXX}则可以添加在导言区,也可以在文章末尾。但注意一定要添加此语句,不然没有bib文件,bibtex编译会报错。

此外,在bib文件中,可能需要更改作者名称。例如我的bibtex是由Zotero导出的IEEE格式,因此会有问题。如果没有正确的格式,否则可能会出现问题(英文作者名称全部大写,且姓名顺序错误)。正确的bibtex文献格式如下(作者的姓名内不能有逗号,且作者名需要用大括号框住):

1
2
3
4
5
6
7
8
9
10
11
12
@article{huttenlocherComparingImagesUsing1993,
title = {Comparing Images Using the {{Hausdorff}} Distance},
author = {{D.P.Huttenlocher} and {G.A.Klanderman} and {W.J.Rucklidge}},
year = {Sept./1993},
journal = {IEEE Transactions on Pattern Analysis and Machine Intelligence},
volume = {15},
number = {9},
pages = {850--863},
issn = {01628828},
doi = {10/c4f5w5},
langid = {english}
}

编译成功后,在参考文献页面会是如下的格式:

1
2
3
[1] D.P.Huttenlocher, G.A.Klanderman, W.J.Rucklidge. Comparing images using the Hausdorff
distance[J/OL]. IEEE Transactions on Pattern Analysis and Machine Intelligence, Sept., 15
(9): 850863. DOI: 10/c4f5w5.

这样就成功了。

在manjaro的使用过程中,因为要经常用到python,所以安装了anaconda用于管理python环境。最开始为了图方便,于是直接在zsh打开时将$(anaconda path)/bin 放在环境变量path中,并且在最前面。但是这样的做法迎来了许多的麻烦。在需要在zsh中对一些源代码进行编译时,经常会出现系统首先检测到anaconda内部的c运行库,这些c运行库由于版本或是依赖问题,导致编译时常常报错。由于最开始很少会遇到这种问题,于是仅仅是临时修改了~/.zshrc 文件,但是后来越来越多的遇到这类问题,常常make出错,也很难定位问题,最终才发现是编译过程中使用了anaconda的c运行库出的问题,于是决定不再每次启动zsh时都导入conda环境。

在~/.zshrc 文件中,将相关的把conda路径加入path的语句删除,新建立以下函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
condainit(){
# >>> conda initialize >>>
# !! Contents within this block are managed by 'conda init' !!
__conda_setup="$('/opt/anaconda3/bin/conda' 'shell.zsh' 'hook' 2> /dev/null)"
if [ $? -eq 0 ]; then
eval "$__conda_setup"
else
if [ -f "/opt/anaconda3/etc/profile.d/conda.sh" ]; then
. "/opt/anaconda3/etc/profile.d/conda.sh"
else
export PATH="/opt/anaconda3/bin:$PATH"
fi
fi
unset __conda_setup
# <<< conda initialize <<<
}

这样当需要在zsh中使用conda环境时,执行condainit就可以导入conda环境。如果需要删除conda环境,即将其从path中删除,由于要删除path中的路径较为麻烦,建议重启zsh,不执行condainit时就不会导入conda环境。

边缘检测算子的系数和

在使用边缘检测算子对图像进行边缘检测的时候,我们会发现,各种算子,例如Roberts算子,Laplace算子,Prewitt算子以及Sobel算子,其掩膜的系数和均为零。而另一方面,对于平滑算子,例如均值滤波器,其掩膜系数的和均为正数,且具有一个归一化系数,使得掩膜系数和为1。平滑算子的归一化系数是为了保证输出图像的灰阶和输入图像的灰阶相同(即8bit -> 8bit),这是很好理解的,因此以下仅讨论边缘检测算子和平滑算子系数和一个为零,一个不为零的原因。

Roberts算子

image-20211120143225626

Laplace算子

image-20211120143311309

Prewitt算子

image-20211120143356633

Sobel算子

image-20211120143450626

均值滤波器(盒状滤波器)

image-20211120143515584

形象的理解

如果简单考虑掩膜本身的性质——因为掩膜本身是需要和图像的像素进行卷积,即对于目标像素而言,是将目标像素与其领域组成的像素集合(集合大小由掩膜大小决定,3x3掩膜决定目标像素和周围8个像素共同组成的3x3像素集合)乘以掩膜的各个系数并最后累加起来,并替换目标像素的值。说的更明白一点,掩膜的卷积,就像是由目标像素和周围的像素共同投票决定目标像素的实际值,而每个像素的投票权利可能不一样——这是由掩膜的系数决定的,每个像素的初始票数也不一样——这是由原始图像的灰度值决定的。

在平滑的环境下,所有的系数都是同样符号的,也即像素集合中的所有像素,或许投票权利大不相同(例如高斯滤波器),但是都是赞成票,即大家的心思都是一个方向的。此外,由于自己的投票会影响到周围像素,高灰度值会被周围的低像素值拉低,低灰度值会被高灰度值拉高,导致像素之间的差距减小了,也最终导致了最后的平滑效果。

而在边缘检测的环境下,这些系数却一部分是正,一部分是负,且赞成票与反对票的权重是相同的——因此最终的结果只能体现在初始票数的不同上。因此掩膜卷积的结果是得到目标像素和其周围像素之间的差距,因此最终导致了边缘的突出(灰度差距越大,边缘越突出)。

从差分的角度

这里使用更数学的表达。由于数字图像是二维的离散信号,因此接下来为了讨论方便,讨论一维的离散信号。

对于一个1D离散信号 $ f(x) $,其一阶差分$Df(x)=f(x+1)-f(x)$,二阶差分$D^2f(x)=D(Df(x))=f(x+2)-2f(x+1)+f(x)$,以此类推。在这里也可以看出来,任意阶差分的表达式中,所有项的系数和为零。而零阶,也就是$f(x)$,其系数和为1。

在1D信号中,我们也可以将$f(x)$看做是目标像素,$f(x+n)$看做是周围像素。

由此,我们可以推论:对于任意一阶与一阶以上的差分函数的线性组合,其系数和总为零;同时,对于任意关于$f(x+n)$的多项式,只要其系数和为零,都可以看成是一些差分函数的线性组合。如果说一阶差分代表原始信号的一阶梯度抽象,二阶差分代表二阶梯度抽象,那么,任意系数为零的多项式,其可分解为部分差分函数的线性组合,也即说明此多项式代表原始信号的部分抽象的线性组合。在这里,多项式的结果即可看做掩膜的卷积结果。

如果多项式的系数和不为零,那么可以分为系数和为零的部分和不为零的部分,其中系数和为零的部分依旧代表抽象;而不为零的部分,其形式为$f(x+n)$的组合,且均同号,这里f(x+n)则是仅仅代表信号在空间中有移动,但是其整体信息依旧是零阶的原始信号的信息。因此,如果多项式的系数和不为零,其必然包含有原始信号的信息。

或许读者会思考到这样一个问题:如果原始图像(2nx2n大小)是左半边白色,右半边为黑色,而多项式$f(x)+f(x+n)$中,除了原始图像外,仅有的一项是图像右移半幅图像的距离,那么叠加的输出图像全为白色,而体现不出原始图像的信息。事实上,出现这样的问题,是因为尺度没有考虑清楚。由于多项式的结果是掩膜的卷积结果,因此很容易想到,如果掩膜的过大(上面出现的问题即为掩膜过大),就会对图像过度平滑(因为这里的多项式系数和不为零,因此掩膜为平滑算子),导致图像信息过度减少。而在实际应用过程中,掩膜的大小通常会远小于图像大小,因此不会造成图像信息的过度减少。

总结

因此,边缘平滑算子掩膜的系数和为零并不是偶然——正是因为我们需要原始图像的差分抽象信息,而不希望输出图像中混杂有原始图像的信息,因此才需要算子的系数和必须为零。反之,如果在图像处理过程中,需要原始图像的信息,设计掩膜时,则算子的系数和不能为零。

原理

快速排序的原理还是比较简单的,就是一个归并的思想,找到一个参考值,然后每一次把子序列分成一边大一边小的两个子序列,而参考值在中间。但是用什么样的方式去写代码将子序列分成两个子序列,同时不会有死循环的可能,并且能够尽量使用少的交换次数,也是一个问题。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void quickSort(vector<int>& v, int start, int end) {
if (start < end) {
int x = v[start];
int i = start;
int j;
for (j = start + 1; j < end; j++) {
if (v[j] <= x) {
i++;
swap(v[i], v[j]);
}

}

swap(v[i], v[start]);
quickSort(v, start, i);
quickSort(v, i + 1, end);
}
}

我使用了how to implement quick sort algorithm in C++ - Stack Overflow 中的方式,这样的代码交换次数很少,而且结构简单,只有一个for循环。

首先代码是一个if的判断,后续的子序列分割只有在满足start < end的情况下才会运作。这个判断使得递归到最底处时能够返回,此时start=end,即子序列只有一个元素。

当子序列没有分割到只剩一个元素时,进行分割过程。为了方便,将第一个元素作为支点(pivot),即作为将序列分成大小两部分的参考值。然后设定ij,其中i为start,即使得v[i]为序列的支点,也可以理解为最初记录的小于等于支点的元素个数为0。之后进行循环,从支点后的第一个元素开始遍历完子序列。每当遇到一个小于等于支点的值,则i+1,通过swap函数交换,即使得这个小于等于支点的元素(v[j])与原来支点右边第一个元素(v[i])交换(由于i始终小于等于j,因此v[i]不是等于v[j]自己,就是大于支点的元素)。经过这样的交换后,i的值加一,可以理解为记录的小于等于支点的元素个数加一,v[i]指向最新的已经交换完成的较小的元素。

当循环完整个子序列后,对于整个子序列,在支点右边的[start+1, end]的部分已经分成了左边小右边大的两部分,且v[i]指向最后一个小于等于支点的元素,正好在大小两个序列的分界处。注意到支点的位置还是在start处,需要交换v[i](此时v[i]是最后一个小于等于支点的元素)和支点的位置,使得参考值正好分隔开左边小于等于它的子序列和右边大于它的子序列。这一步很容易被忽略,但是必不可少,因为算法中要求参考值必须在中间。

这样就完成了将子序列分成大小两部分的过程,其中参考值左边是较小的部分(并把参考值看作是较小的这一部分),右边是较大的部分。然后分别把这两部分继续递归分割。

这样,快速排序的代码就完成了。