图像表格实线和虚线检测

文章目录

  • 1 背景简述
  • 2 camelot中的方法
    • 2.1 二值化
    • 2.2 腐蚀膨胀
    • 2.3 轮廓检测
    • 2.4 结果展示
  • 3 基于霍夫直线检测的方法
    • 3.1 霍夫直线检测原理
    • 3.2 概率霍夫直线检测
    • 3.3 霍夫直线应用
  • 参考资料

1 背景简述

图像中的表格结构化是一个比较热门的话题,其输入是一张图片,输出是结构化过的所有表格,也可以认为输出的是一个excel。目前市面上也没有哪家做的比较完美,因为表格总是千奇百怪的。不过对于一些简单规整的有线表或者多线表,还是可以做到比较好的结构化的。

图像表格检测的一般流程为
图像表格检测流程

图1-1 图像表格检测流程图

【表格检测】是为了找到图像上的表格位置,同时分开一些挨得比较近的表,需要训练一个图像检测的模型,这个标一批数据硬train就行了,Yolov5等一般的图像检测模型效果都不错。不过对于只有一行的表,检测效果不太好,这个得要传统方法的辅助。

【水平线和垂直线检测】是为了检测表格中的分割线,对表格结构化有很大的参考意义。某些单行表也可以通过这步的结果来判断。或者说四边有线的,就可以用这里的结果来判断表格的位置。这篇讲的就是这一步。

【OCR】是为了得到表格中每个单元格的文本。

【表格结构化】是结合前三个模块的结果,得到结构化的表格,这里根据要处理的业务场景中表格的多样性程度,会有不同代码量的规则。我处理的场景得要写了几千行的规则了。

这篇只讲讲怎么把图像中的表格线给检测出来。

方案主要有两种:
(1)二值化+腐蚀膨胀+轮廓检测,这是camelot中使用的方法。
(2)边缘检测+霍夫直线检测,这是网上见到比较多的方法。

下面就来说说这两种方法,所使用的图片就是
测试图片样例

图1-2 测试图片样例

取这张图片是因为图中的表格又有实线,又有虚线。方便比较不同方法的效果。

2 camelot中的方法

2.1 二值化

二值化之用了opencv当中的cv2.adaptiveThreshold,这种二值化的方法可以根据局部的色差来自适应调整阈值,比较符合表格背景色花里胡哨的场景。

def adaptive_threshold(imagename, process_background=False, blocksize=15, c=-2):"""Thresholds an image using OpenCV's adaptiveThreshold.Parameters----------imagename : stringPath to image file.process_background : bool, optional (default: False)Whether or not to process lines that are in background.blocksize : int, optional (default: 15)Size of a pixel neighborhood that is used to calculate athreshold value for the pixel: 3, 5, 7, and so on.For more information, refer `OpenCV's adaptiveThreshold <https://docs.opencv.org/2.4/modules/imgproc/doc/miscellaneous_transformations.html#adaptivethreshold>`_.c : int, optional (default: -2)Constant subtracted from the mean or weighted mean.Normally, it is positive but may be zero or negative as well.For more information, refer `OpenCV's adaptiveThreshold <https://docs.opencv.org/2.4/modules/imgproc/doc/miscellaneous_transformations.html#adaptivethreshold>`_.Returns-------img : objectnumpy.ndarray representing the original image.threshold : objectnumpy.ndarray representing the thresholded image."""img = cv2.imread(imagename)gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)if process_background:threshold = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, blocksize, c)else:threshold = cv2.adaptiveThreshold(np.invert(gray),255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,cv2.THRESH_BINARY,blocksize,c,)return img, threshold

使用的时候,直接用就行

image, threshold = adaptive_threshold(image_path,process_background=False,blocksize=11,c=-2,
)

这里的threshold就是二值化之后的图像。

2.2 腐蚀膨胀

腐蚀膨胀的目的是把沿水平和竖直方向的长线给找出来。腐蚀是当kernel范围内有0时,就全部置0,滤掉了不连续的像素点;膨胀是把kernel中心为255的点膨胀成kernel的大小,把原来线段上被腐蚀的点给还原回来。

举个例子,我们先构造一个垂直方向长度为5的kernel。

import cv2
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1, 5))

kernel为

array([[1],[1],[1],[1],[1]], dtype=uint8)

情况一:
我们先构造一个数值方向长度不足5的直线,并用kernel腐蚀一下

import numpy as np
img = np.zeros((10, 5))
img[1:5, 0] = 255
erode_img = cv2.erode(img, kernel)

img为

array([[  0.,   0.,   0.,   0.,   0.],[255.,   0.,   0.,   0.,   0.],[255.,   0.,   0.,   0.,   0.],[255.,   0.,   0.,   0.,   0.],[255.,   0.,   0.,   0.,   0.],[  0.,   0.,   0.,   0.,   0.],[  0.,   0.,   0.,   0.,   0.],[  0.,   0.,   0.,   0.,   0.],[  0.,   0.,   0.,   0.,   0.],[  0.,   0.,   0.,   0.,   0.]])

erode_img为

array([[0., 0., 0., 0., 0.],[0., 0., 0., 0., 0.],[0., 0., 0., 0., 0.],[0., 0., 0., 0., 0.],[0., 0., 0., 0., 0.],[0., 0., 0., 0., 0.],[0., 0., 0., 0., 0.],[0., 0., 0., 0., 0.],[0., 0., 0., 0., 0.],[0., 0., 0., 0., 0.]])

情况二:
我们再构造一个数值方向长度足够5的直线,并用kernel腐蚀一下

import numpy as np
img = np.zeros((10, 5))
img[1:6, 0] = 255
erode_img = cv2.erode(img, kernel)

img为

array([[  0.,   0.,   0.,   0.,   0.],[255.,   0.,   0.,   0.,   0.],[255.,   0.,   0.,   0.,   0.],[255.,   0.,   0.,   0.,   0.],[255.,   0.,   0.,   0.,   0.],[255.,   0.,   0.,   0.,   0.],[  0.,   0.,   0.,   0.,   0.],[  0.,   0.,   0.,   0.,   0.],[  0.,   0.,   0.,   0.,   0.],[  0.,   0.,   0.,   0.,   0.]])

erode_img为

array([[0., 0., 0., 0., 0.],[0., 0., 0., 0., 0.],[0., 0., 0., 0., 0.],[255., 0., 0., 0., 0.],[0., 0., 0., 0., 0.],[0., 0., 0., 0., 0.],[0., 0., 0., 0., 0.],[0., 0., 0., 0., 0.],[0., 0., 0., 0., 0.],[0., 0., 0., 0., 0.]])

膨胀的话,就可以把没有完全被腐蚀掉的点,恢复回线,这里就不举例啰嗦了。

2.3 轮廓检测

轮廓检测部分是把腐蚀膨胀得到的线给找出来,这个和腐蚀膨胀在同一个函数里

def find_lines(threshold, regions=None, direction="horizontal", line_scale=15, iterations=0
):"""Finds horizontal and vertical lines by applying morphologicaltransformations on an image.Parameters----------threshold : objectnumpy.ndarray representing the thresholded image.regions : list, optional (default: None)List of page regions that may contain tables of the form x1,y1,x2,y2where (x1, y1) -> left-top and (x2, y2) -> right-bottomin image coordinate space.direction : string, optional (default: 'horizontal')Specifies whether to find vertical or horizontal lines.line_scale : int, optional (default: 15)Factor by which the page dimensions will be divided to getsmallest length of lines that should be detected.The larger this value, smaller the detected lines. Making ittoo large will lead to text being detected as lines.iterations : int, optional (default: 0)Number of times for erosion/dilation is applied.For more information, refer `OpenCV's dilate <https://docs.opencv.org/2.4/modules/imgproc/doc/filtering.html#dilate>`_.Returns-------dmask : objectnumpy.ndarray representing pixels where vertical/horizontallines lie.lines : listList of tuples representing vertical/horizontal lines withcoordinates relative to a left-top origin inimage coordinate space."""lines = []if direction == "vertical":size = threshold.shape[0] // line_scaleif size < 2:size = threshold.shape[0]el = cv2.getStructuringElement(cv2.MORPH_RECT, (1, size))elif direction == "horizontal":size = threshold.shape[1] // line_scaleif size < 2:size = threshold.shape[1]el = cv2.getStructuringElement(cv2.MORPH_RECT, (size, 1))elif direction is None:raise ValueError("Specify direction as either 'vertical' or 'horizontal'")if regions is not None:region_mask = np.zeros(threshold.shape)for region in regions:x, y, w, h = regionregion_mask[y : y + h, x : x + w] = 1threshold = np.multiply(threshold, region_mask)threshold = cv2.erode(threshold, el)threshold = cv2.dilate(threshold, el)dmask = cv2.dilate(threshold, el, iterations=iterations)try:contours, _ = cv2.findContours(threshold.astype(np.uint8), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)except ValueError:# for opencv backward compatibility_, contours, _ = cv2.findContours(threshold.astype(np.uint8), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)for c in contours:x, y, w, h = cv2.boundingRect(c)x1, x2 = x, x + wy1, y2 = y, y + hif direction == "vertical":lines.append(((x1 + x2) // 2, y1, (x1 + x2) // 2, y2))elif direction == "horizontal":lines.append((x1, (y1 + y2) // 2, x2, (y1 + y2) // 2))return dmask, lines

横线和竖线的检测代码为

# 竖线检测
iterations = 0
vertical_line_scale = 60
regions = None
vertical_mask, vertical_segments = find_lines(threshold,regions=regions,direction="vertical",line_scale=vertical_line_scale,iterations=iterations,
)# 横线检测
iterations = 0
horizontal_line_scale = 50
regions = None
horizontal_mask, horizontal_segments = find_lines(threshold,regions=regions,direction="horizontal",line_scale=horizontal_line_scale,iterations=iterations,
)

通过控制vertical_line_scale和horizontal_line_scale可以控制最小线段长度。vertical_mask和horizontal_mask是二值图像,vertical_segments和horizontal_segments是线段的位置。

2.4 结果展示

按这种方法检测出来的线段如下图2-1所示。
结果展示图

图2-1 结果展示图

不难看出,这种方法下,需要的实线都找到了,但是某些大字上的笔画也被认为是线段,更严重的是,虚线检测不出来。

在表格中没有虚线的场景下,这其实是一个简单快捷准确的方案。

3 基于霍夫直线检测的方法

为了解决虚线没法检测出来的问题,就想到了霍夫直线检测。这里简单说明一下霍夫直线检测是怎么回事。

3.1 霍夫直线检测原理

霍夫直线检测想明白了很简单,初中的知识就能解了。

要想明白这个问题,首先得要知道过xy坐标系上的某个点(x0,y0)(x_0, y_0)(x0,y0)的所有直线如何表示。我们都知道,一条直线可以用斜率kkk和截距bbb唯一确定为y=kx+by=kx+by=kx+b。我们再构造一个kb坐标系,横轴为kkk,纵轴为bbb。那么这个坐标系上的任意一点(ki,bi)(k_i, b_i)(ki,bi)就是xy坐标系上的一条直线。再说一遍,kb坐标系上的一个点,就代表了xy坐标系上的一条直线

那么好了,过(x0,y0)(x_0, y_0)(x0,y0)的所有直线在kb坐标系上就是直线

y0=kx0+b→{k=−1x0b+y0x0ifx0≠0b=y0ifx0=0(3-1)y_0 = kx_0 + b \rightarrow \begin{cases} k = -\frac{1}{x_0}b + \frac{y_0}{x_0} &if\ x_0 \ne 0\\ b = y_0 &if\ x_0 =0 \end{cases} \tag{3-1} y0=kx0+b{k=x01b+x0y0b=y0if x0=0if x0=0(3-1)

kb坐标系上的一条直线,就是过xy坐标系上的某个点的所有直线

同理,假设有另一个点(x1,y1)(x_1, y_1)(x1,y1),过该点的所有直线在kb坐标系上的直线为y1=kx1+by_1=kx_1+by1=kx1+b

方程组(3−2)(3-2)(32)的解(k∗,b∗)(k^*, b^*)(k,b),就是过(x0,y0)(x_0, y_0)(x0,y0)(x1,y1)(x_1, y_1)(x1,y1)这两点所确定的直线。

{y0=kx0+by1=kx1+b(3-2)\begin{cases} y_0 = kx_0 + b \\ y_1=kx_1+b \end{cases} \tag{3-2} {y0=kx0+by1=kx1+b(3-2)

xy坐标系同一直线上的所有点的所有直线的表示,在kb坐标系上必定都过同一个点,如下图3-1所示。图是从参考资料[2]借过来,所以符号不一致,懒得画了。
xy和kb空间映射图

图3-1 xy和kb空间映射图

这样以来,我们就可以根据kb空间上某个点被多少条直线穿过来判断在xy坐标系上有多少个点在这条直线上。

不过映射到kb坐标系会有一个问题,当xy坐标系上的直线接近于平行y轴时,k也会接近于无穷大,无穷大就没法算了。为了解决这个问题,就提出了把xy空间映射到极坐标θr\theta rθr空间。

映射方法如下图3-2所示,这图是借的参考资料[3]的。xy坐标系中的每条直线都θr\theta rθr空间中的一个点(θ,r)(\theta, r)(θ,r)rrr为xy坐标系中原点距离直线的距离,θ\thetaθ表示原点到直线的垂线与x轴的夹角;xy坐标系中过某个点的所有直线都对应于θr\theta rθr空间中的一条曲线r=xicosθ+yisinθr = x_i cos\theta + y_i sin\thetar=xicosθ+yisinθ
xy和极坐标映射图

图3-2 xy和极坐标空间映射图

如果这里想不明白为啥r=xicosθ+yisinθr = x_i cos\theta + y_i sin\thetar=xicosθ+yisinθ可以表示xy空间过(xi,yi)(x_i, y_i)(xi,yi)的所有直线。不妨这样想一下,某条直线过图3-2中的点(x2,y2)(x_2, y_2)(x2,y2),刚开始是和y轴平行的,即θ=0\theta=0θ=0,然后开始绕(x2,y2)(x_2, y_2)(x2,y2)旋转,θ\thetaθ不断变大,直到转过2π2\pi2π。这个转动的过程遍历了所有过(x2,y2)(x_2, y_2)(x2,y2)的直线,而且动手画辅助线算算看的话,会发现rrr一直满足r=x2cosθ+y2sinθr = x_2 cos\theta + y_2 sin\thetar=x2cosθ+y2sinθ

与kb坐标系不同,这里θ\thetaθ就是[0,2π)[0, 2\pi)[0,2π)的取值范围,rrr只要点(xi,yi)(x_i, y_i)(xi,yi)离原点是有限距离的就行,这个在实际场景都能满足。不会产生无限大的值。

至于怎么找直线,也是和kb坐标系一样,在θr\theta rθr空间找很多条曲线相交的那个点,就是xy空间的直线。点数量设置一个阈值,不让太短的线进来就行。

霍夫直线的好处是可以找到虚线。

3.2 概率霍夫直线检测

霍夫直线检测一般需要先把图像过边缘检测(比如canny),然后再将所有的边缘点映射到θr\theta rθr空间后寻找被超过一定数量的曲线相交的那些点。这样有两个缺点,一是计算量太大,二是不知道线段的真实长度。所以就有了概率霍夫直线检测。

概率霍夫会取边缘点的一个子集,来进行θr\theta rθr空间交点的统计,有一个累加器(Hough accumulator)会统计候选点被曲线穿过的次数。这大大减小了计算量,根据参考资料[6]说的,只要2%的边缘点,就有比较好的效果了。由于使用的是子集,所以点数量的阈值也要相应地调小。

根据阈值确定了候选点之后,概率霍夫会去边缘点的全集上找还有哪些点也在这条直线上,并发间隔太大的点过滤掉,这样以来就可以找到一条线段上的所有点了。

这样以来计算量大和不知道线段真实长度的问题就都解决了。

3.3 霍夫直线应用

霍夫直线检测在opencv中对应于cv2.HoughLines这个函数,只返回θ\thetaθrrr。概率霍夫在opencv中对应于cv2.HoughLinesP这个函数,返回线段的起始点和终止点坐标。这两个函数的参数说明可以参考参考资料[7],这里就不说了。

直接上代码,总的来说就两步,先Canny边缘检测,再概率霍夫。

import cv2im = cv2.imread(image_path)
gray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 150, 200, apertureSize=3)
img = im.copy()
lines = cv2.HoughLinesP(edges, 1, np.pi/180, 100, minLineLength = 100, maxLineGap = 10)

这样得到的结果如下图3-3所示。
概率霍夫结果

图3-3 概率霍夫结果图

不难看出,虚线出来了,但是有三个问题,一是文字的笔画也被认为是直线了,二是有挺多接近于重合的直线,三是有斜线出现。这几个问题都可以通过后处理来解决。

ocr的结果可以提供文字的位置,那些文字上的直线可以用这个信息过滤掉;接近于重合的直线可以根据直线的距离过滤掉;斜线根据斜率过滤掉即可,用表格检测的检测框也能过滤掉一大波线,因为我们只要表格里的表格线。

整体来说,方法总比困难多。

参考资料

[1] https://github.com/atlanhq/camelot
[2] https://blog.csdn.net/lkj345/article/details/50699981
[3] https://congleetea.github.io/blog/2018/09/28/hough-transform/
[4] https://stackoverflow.com/questions/59340367/how-does-the-probabilistic-hough-transform-compute-the-end-points-of-lines
[5] https://blog.csdn.net/zhaocj/article/details/40047397
[6] https://jayrambhia.com/blog/probabilistic-hough-transform
[7] https://www.cxyzjd.com/article/hihell/113670582

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/470547.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

二十四、PHP框架Laravel学习笔记——模型的数据集合

一&#xff0e;数据集合 数据集合&#xff0c;就是已经将模型方法 get()获取到的数据再进行处理&#xff1b;比如&#xff1a;map()方法&#xff0c;通过它可以实现类似访问器一样对字段进行处理的效果&#xff1b; $users User::get(); //使用集合方法 map 可以对输出的字…

论文阅读 - AUTOVC: Zero-Shot Voice Style Transfer with Only Autoencoder Loss

文章目录1 概述2 模型架构3 模块解析3.1 获取梅尔频谱3.2 speaker encoder3.3 AutoVC3.4 Vocoder4 关键部分参考资料1 概述 voice conversion这个任务的目标是输入两个音频&#xff0c;其输入是两段音频&#xff0c;一段音频称为content_audio&#xff0c;另一段称为speaker_a…

二十五、PHP框架Laravel学习笔记——模型的一对一关联

一&#xff0e;关联概念 关联模型&#xff0c;即&#xff1a;两张或以上的表进行一定规则的绑定关联&#xff1b;比如&#xff1a;一个学生(学生表)对应一张个人信息卡(信息表)&#xff0c;这种就是一对一&#xff1b;再比如&#xff1a;一篇博文(帖子表)对应多个评论(评论表)…

小工具:基于颜色的视频和图片切割

文章目录1 前言2 方案简述3 效果1 前言 最近做一个短视频相关的项目的时候&#xff0c;发现输入的视频有很多是有黑边的&#xff0c;有些可能是白边或者其他颜色的边。这对下游的模型处理有很大的影响。于是就写了一个自动判断填充边的颜色&#xff0c;并根据该颜色自动切割视…

二十六、PHP框架Laravel学习笔记——模型的一对多关联

二&#xff0e;一对多关联 一对多关联&#xff0c;本质上使用方法和一对一关联类似&#xff0c;内部实现略有不同&#xff1b; 创建另一个模型&#xff1a;book.php&#xff0c;我们看下这个表数据&#xff1b; PS&#xff1a;这里 user_id19 有三个&#xff0c;也就是蜡笔小…

论文阅读 - An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale

文章目录1 概述2 方法简述2.1 encoder之前2.2 encoder之后3 实验结果参考资料1 概述 这篇论文是一篇将tranformer引入到图像领域的里程碑式的文章。因为这是第一次在处理图像时&#xff0c;将所有的卷积模块统统抛弃&#xff0c;只使用attention。并且实验证明了只用attention…

python五子棋人机对战_Python:游戏:五子棋之人机对战

原标题&#xff1a;Python&#xff1a;游戏&#xff1a;五子棋之人机对战 开端 画棋盘 首先肯定是要画出棋盘来&#xff0c;用 pygame 画出一个 19 19 或 15 15 的棋盘并不是什么难事&#xff0c;这在之前的文章中已经多次用到&#xff0c;就不赘述了。 画棋子 需要说一下的是…

二十七、PHP框架Laravel学习笔记——模型的多对多关联

二&#xff0e;多对多关联 多对多关联&#xff0c;比前面两种要复杂一些&#xff0c;需要一张中间表&#xff0c;共三张&#xff1b; (1) .users&#xff1a;用户表&#xff1b; (2) .roles&#xff1a;权限表&#xff1b; (3) .role_user&#xff1a;中间表&#xff1a;默…

论文阅读 - Is Space-Time Attention All You Need for Video Understanding?

文章目录1 概述2 模型结构2.1 模型输入2.2 attention模块2.3 分类模块3 模型分析3.1 不同attention方式3.2 不同的输入3.3 不同的模型3.4 不同的预训练数据3.5 不同的数据量3.6 position embedding的影响3.7 长输入时长3.8 不同的transformer3.9 不同的patch size3.10 attentio…

iOS中常见的6种传值方式,UIPageViewController

通过属性传值、方法传值、代理传值、Block传值、单例传值、通知传值6种方式进行不同视图之间的传值。不同方式只需要在AppDelegate中更改下UINavigationController的根控制器即可。使用很简单的实例让你很快理解不同的传值方式。 UIPageViewController(上传者&#xff1a;JoneJ…

websocket 获取ip_Spark+Kafka+WebSocket+eCharts实时分析-完全记录(1)

本系列内容&#xff1a;Kafka环境搭建与测试Python生产者/消费者测试Spark接收Kafka消息处理&#xff0c;然后回传到KafkaFlask引入消费者WebSocket实时显示版本&#xff1a;spark-2.4.3-bin-hadoop2.7.tgzkafka_2.11-2.1.0.tgz------------------------第1小节&#xff1a;Kaf…

二十八、PHP框架Laravel学习笔记——模型的关联查询

二&#xff0e;关联查询 前几篇博文&#xff0c;了解了三种基础的关联模型&#xff0c;并简单的进行查询&#xff1b;本节课&#xff0c;我们详细的了解更多的查询方案&#xff1b; //下面两种查询是一样的&#xff1b; $books User::find(19)->book; $books User::fin…

搞懂CRF

文章目录1 前言2 Log-linear model3 MEMM3.1 模型概述3.2 label bias问题4 CRF4.1 模型概述4.2 模型训练4.3 模型解码4.4 小结参考资料1 前言 条件随机场(conditional random field, CRF)是在建立序列模型时的常用模块&#xff0c;它的本质就是描述观测到的序列xˉ\bar{x}xˉ对…

skywalking 安装_SkyWalking全链路追踪利器

随着目前系统架构的复杂度越来越高(中台、微服务)&#xff0c;并且线上应用的多级监控覆盖到了通讯、应用处理过程监控并且实现端到端的应用监测&#xff0c;线上性能故障的快速定位修复&#xff1b;而传统的监控分析方式已经无法满足我们的需求&#xff0c;因此许多强大的APM工…

二十九、PHP框架Laravel学习笔记——Debugbar 调试器

二&#xff0e;安装使用 通过 composer 在项目中安装 Debugbar&#xff0c;命令如下&#xff1a; composer require barryvdh/laravel-debugbar 生成一个配置文件&#xff0c;给用户配置&#xff0c;可以根据需求进行配置&#xff1b; php artisan vendor:publish --provider…

论文阅读 - Video Swin Transformer

文章目录1 概述2 模型介绍2.1 整体架构2.1.1 backbone2.1.2 head2.2 模块详述2.2.1 Patch Partition2.2.2 3D Patch Merging2.2.3 W-MSA2.2.4 SW-MSA2.2.5 Relative Position Bias3 模型效果参考资料1 概述 Vision Transformer是transformer应用到图像领域的一个里程碑&#x…

rocketmq queue_RocketMQ 实战(三) - 消息的有序性

■ RocketMQ有序消息的使用1 为什么需要消息的有序性比如用户张三终于挣了一百存在在银行卡里存取款,对应两个异步的短信消息,肯定要保证先存后取吧,不然都没钱怎么发了取钱的消息呢! M1 - 存钱 M2 - 取钱而mq默认发消息到不同q显然是行不通的,会乱序 需要发往同一个q,先进先出…

三十、PHP框架Laravel学习笔记——模型的预加载

一&#xff0e;预加载 预加载&#xff0c;就是解决关联查询中产生的 N1 次查询带来的资源消耗我们要获取所有书籍的作者(或拥有者)&#xff0c;普通查询方案如下&#xff1a; //获取所有书籍列表 $books Book::all(); //遍历每一本书 foreach ($books as $book) { //每一本…

论文阅读:Spatial Transformer Networks

文章目录1 概述2 模型说明2.1 Localisation Network2.2 Parameterised Sampling Grid3 模型效果参考资料1 概述 CNN的机理使得CNN在处理图像时可以做到transition invariant&#xff0c;却没法做到scaling invariant和rotation invariant。即使是现在火热的transformer搭建的图…

dataframe 排序_疯狂Spark之DataFrame创建方式详解一(九)

创建DataFrame的几种方式1、读取json格式的文件创建DataFrame注意&#xff1a;1. json文件中的json数据不能嵌套json格式数据。2. DataFrame是一个一个Row类型的RDD&#xff0c;df.rdd()/df.javaRdd()。3. 可以两种方式读取json格式的文件。4. df.show()默认显示前20行数据。5.…