opencv 图像 抠图 算法_图像抠图算法学习 - Shared Sampling for Real-Time Alpha Matting

一、序言

陆陆续续的如果累计起来,我估计至少有二十来位左右的朋友加我QQ,向我咨询有关抠图方面的算法,可惜的是,我对这方面之前一直是没有研究过的。除了利用和Photoshop中的魔棒一样的技术或者Photoshop中的选区菜单中的色彩范围类似的算法(这两个我有何PS至少90%一致的代码)是实现简单的抠图外,现在一些state of art 方面的算法我都不了解。因此,也浪费了不少的将知识转换为资产的机会。年30那天,偶然的一个机会,有位朋友推荐我看了一篇关于抠图的文章,并有配套的实现代码,于是我就决定从这篇文章开始我的抠图算法研究之旅。

这篇文章就是Shared Sampling for Real-Time Alpha Matting,关于这篇文章的一些信息,可以在这个网站里找到很多:http://www.inf.ufrgs.br/~eslgastal/SharedMatting/,配套的一个代码在CSDN中可以下载,具体见:http://download.csdn.net/detail/jlwyc/4676516

这篇文章的标题很具有吸引力,发表日期为2010,也算是比较新的。在大家继续看下去之前,我要提醒的是,这里的Real - Time有比较多的限制:主要是(1)必须依赖于强劲的GPU;(2)应用的抠图场合的背景应该比较简单。

不管如何,因为有配套的实现代码,作为起步的研究来说,该文还是算不错的。

从目前流行的抠图技术来看,这篇文章的思路算是比较落伍的一种。

二、技术细节

好了,不管那么多,我先贴些论文中的公式及一些说明将文章的主体细路描述一下。

简单的说,抠图问题就是要解决如下的一个超级病态的方程:

式中:Cp是我们观察到的图像的颜色,FP、BP、αp均是未知量,可分别称之为前景、背景及透明度。

要解决这样的一个病态的方程,就必须给其增加一些附加的约束,通常,这种约束可以是和待分割图像同等大小的TriMap或者是用户收工划定的scribbles的形式存在,如下两图所示(如未特别说明,一般白色部分表示前景,黑色表示背景,灰色表示待识别的部分):

     

TriMap                                 scribbles

这样的约束条件使得我们知道了那一部分是明确属于前景(αp=1),而那一部分是属于背景(αp=0),那么下面的主要任务就是搞定那些未知区域的αp值 。

按照论文的说法,在2010年前后解决matting问题的主要方法是基于sampling, pixel affinities 或者两者的结合,特别是后两种是主流的方式。但是这两种都需要求解一个大型的线性系统,这个系统的大小和未知点的个数成正比(我简单看了下closed form那篇抠图文档的代码,就用到了一个庞大的稀疏矩阵),因此对于1MB左右大小的图,求解时间在几秒到几分钟不等。这篇论文提出的算法应该说是基于sampling技术的,他充分利用了相邻像素之间的相似性,并利用了算法内在的并行性,结合GPU编程,实现抠图的实时展示。

总的来说,论文提出的算法可以分成4个步骤:

第一步:Expansion,针对用户的输入,对已知区域(前景或背景)进行小规模的扩展;

第二步:Sample and Gather,对剩余的未知区域内的每个点按一定的规则取样,并选择出最佳的一对前景和背景取样点;

第三步:Refinement,在一定的领域范围内,对未知区域内的每个点的最佳配对重新进行组合。

第四步:Local Smoothing,对得到的前景和背景对以及透明度值进行局部平滑,以减少噪音。

2.1  Expansion

这一步,按照我的经验,可以不做,他唯一的作用就是减少未知点的个数,可能在一定程度上减小后期的计算量,原理也很简单,就是对一个未知点,在其一定的邻域半径内(文中推荐值10,

并且是圆形半径),如果有已知的背景点或前景点,则计算其颜色和这些已知点颜色的距离,然后把这个未知点归属于和其颜色距离小于某个值并且最靠近该点的对象(前景或背景)。

在CSDN提供的参考代码中,这一部分的编码其实写的还是很有特色的,他的循环方式不同于我们普通的邻域编码,他是从像素点逐渐向外部循环开来,有点类似左图的这种循环方式(实际上还是有点区别的,实际是上下两行一起处理,在左右两列处理,然后再向外层扩散),这种处理方式的明显好处就是,只要找到某个点颜色距离小于设定的值,就可以停止循环了,因为这个点肯定是第一个符合颜色距离条件又同时符合物理距离最小的要求的。

这一步做不做,最最终的结果又一定的影响,但是他不具有质的影响。

2.2  Sample and Gather

总的来说,这一步是算法的核心部分,也是对结果影响最大的,他的步骤说起来其实也很简单,我们先看下图。

在这个图中,P和q点都是未知区域,我们需要通过一定的原则在已知区域为其取得一定的样本对,论文中提出的提取方法是:

设定一个参数Kg,其意义为一个点最多可能取样的前景点和背景点的个数,也就意味着最多的取样对为Kg*Kg组,通常这个值可以取为4或者更多,论文建议取4就可以了,越大则程序越耗时。

这样对于每个未知点,从该点出发,引出Kg条路径,每个路径之间成360/Kg的夹角,记录下每条路径经过的路线中首次遇到的前景或背景点,直到超出图像的边缘。

为了算法的稳定性,每3*3的矩形区域内(4*4或者5*5也没说不可以的),路径的起始角度周期性的改变,这样相邻像素的Kg条路径经过的区域就有着较大的不同能得到更为有效的结果集。

由上图可以看到,在不少情况下,未知点的前景和背景取样数并不能达到Kg个,甚至极端情况下,找不到任何一个取样点,这样该点就无法进行透明度的计算了,这就要靠后面的过程了。

 

 

前景取样点数量分布                    背景取样点数量分布                前景+背景取样点数量分布

上图绘制了前面列举的TriMap图中未知区域每个部位的取样点数量分布情况,颜色越靠近白色,表明取样点的数量越大,从图中可以明显看出,处于图像角落的一些未知点取样情况并不是特别理想,但基本上未出现没有取到样的情况,那我们在来看看scribbles那张图的结果。

 

 

前景取样点数量分布背景取样点数量分布前景+背景取样点数量分布

特别是前景取样分布的结果似乎不太令人满意,有些部分取样数为0了,这个问题下面还会谈到。

在完成取样计算后,我们就需要找出这些取样点中那些是最佳的组合,这个时候就涉及到一般优化时常谈到的目标函数了,在这篇论文中,对目标函数用了四个小函数的乘积来计算,分别如下:

1:

其中

为了全面,我们将上式中αp的计算公式列出:

公式(2)的道理很为明显,用一对F/B算出的α值如果很合理的话,那么用α结合F/B重新计算出的颜色应该和原始颜色的差距很小。公式(3)在表明在一定的领域内,由于像素一般不会有突变,差值的平均值也应该很小。

为方便理解,我贴出计算α的部分代码:

///

///通过当前点、前景点以及背景点的颜色值计算对应的Alpha值,对应论文的公式(12)。///

/// 当前点的BGR颜色分量值。

/// 前景点的BGR颜色分量值。

/// 背景点的BGR颜色分量值。

/// Alpha会出现不在[0,1]区间的情况,因此需要抑制。

double CalcAlpha(int BC, int GC, int RC, int BF, int GF, int RF, int BB, int GB, intRB)

{double Alpha =(double) ((BC - BB) * (BF - BB) + (GC - GB) * (GF - GB) + (RC - RB) * (RF - RB)) /((BF- BB) * (BF - BB) + (GF - GB) * (GF - GB) + (RF - RB) * (RF - RB) + 0.0000001); //这里0.0000001换成Eps在LocalSmooth阶段似乎就不对了,有反常的噪点产生

if (Alpha > 1)

Alpha= 1;else if (Alpha < 0)

Alpha= 0;returnAlpha;

}

2: 作者考虑在未知点到取样的前景和背景点之间的直线路径上,应该尽量要少有像素的突变,比如如果这条路径需要经过图像的边缘区域,则应该设计一个函数使得该函数的返回值较大,于是作者使用了下面的公式:

上式即沿着路径对像素颜色进行积分,离散化后也就是一些累加,CSDN的提供的代码在这个函数的处理过程中是有错误的,因为他最后一个判断条件使得循环只会进行一次,有兴趣的朋友可以自己去改改。

按照公式(4)的意义,一个未知点属于前景的可能性可由下式表示:

而一个好的组合也应该最小化下式:

3、未知点和前景点之间的物理距离,一个好的组合中的前景点应该要尽量靠近未知点;

4、未知点和背景点之间的物理距离,一个好的组合中的背景点也应该要尽量靠近未知点;

将这四个条件组合起来,最终得到如下的目标函数:

各子项的指数数据可详见论文本身。

按照这个要求,对前面进行取样得到数据进行处理,并记录下使上式最小的那一对组合,就初步确定了最佳的取样点。

其实,这个时候我们也就可以初步获得处理后的α值了,比如对于我们前面所说的Trimap,其原始图像及经过sample和gather处理后的结果如下图:

   

从处理结果看,已经可以粗略的得到处理的效果了。

2.3、Refinement

初步的gather处理后,正如前文所说,得到的结果还不够细腻,并且有些未知点由于采样的过程未收集到有效的前景和背景数据,造成该点无法进行处理,因此,在Refinement阶段需要进一步解决这个问题。

论文提出,首先,在一定的邻域内,比如半径为5的领域内,首先统计出公式(2)对应的MP值最小的3个点相关颜色数据,并对这些数据进行加权平均,得到数据对:

然后按照下面这些公式计算新的前景、背景、透明度及可信度的计算。

可信度的计算是为下一步的局部平滑做准备的,他反应了我们在这一步确定的取样点是否合理程度的一个度量,经由此步骤,我们可得到的透明度和合成图如下所示:

   

可见在这一步得到的结果对于上图来说已经相当完美了。

2.4 Local Smoothing

这一步说实在的我没有花太多的精力去看,他的实现过程大概有点类似于高斯模糊,但里面多了很多其他方面的处理,一个很好的事情就是在CSDN提供的代码中对这部分每个公式的实现都是正确的,也是完整的,因此,有兴趣的朋友需要自己多看下论文和对应的代码了。

三、算法的效果

按照论文提供的相关资料集我自己搜集的一些图及配套的Trimap测试了该算法的一些结果,现贴出如下所示:

  

  

  

  

  

  

  

  

  

  

  

  

  

  

原图                        Trimap                      合成后的效果图

可见,对于这些Trimap图,在很多情况下是能获得较为满意的效果的。

我还找了一些简单的图,使用scribble的方式进行处理,效果如下所示:

  

  

  

  

  

 

  

  

  

  

原图                            操作界面                            结果图

我选的这些都是背景比较简单的图,因此还能获得较为理想的效果,如果是比较复杂的图,使用scribble是基本上获取不到很理想的效果的,除非人工仔细的划分边界。

四、编程实现

在编程实现方面,CSDN提供的那个代码基本的意思已经达到了,并且里面的函数意义也非常清晰,只不过他使用的opencv的库的,相信专心去研究抠图的人,把他改成其他语言也不是个难题,比如里面用到的vector在C#中就可以用list代替,那些Opencv的结构体也可以在C#中重新定义。

不过那个代码占用的内存非常厉害,这主要是由于VECTOR等数据类型决定的,实际上这里完全可以用数组来搞定的。

我贴一部分代码大家看看:

///

///对每个未知区域的像素按设定的循环角度搜索有效的前景和背景取样点///

/// 未知区域的X坐标。

/// 未知区域的Y坐标。

/// 用于保存前景点的内存区域。

/// 用于保存背景点的内存区域。

/// 最终获取的前景点的数量。

/// 最终获取的背景点的数量。

/// 对于有些点,是有可能获取不到有效的点的。

void Sample(int X, int Y, Point *F, Point *B, int &CountF, int &CountB)

{intZ, XX, YY, Alpha;boolF1, F2;doubleInitAngle, IncAngle, Angle;doubleDx, Dy, Step, Value, XD, YD, StepX, StepY;

IncAngle= 360 / KG; //每次扫描增加的角度

InitAngle = (Y % 5 * 5 + X % 25) * IncAngle / 25; //起始角度,范围在[0,IncAngle]之间,按照3*3的方式轮流替换,有利于提供结果的稳定性,如果KG取值较大,也可以使用4*4或者5*5

CountF = 0; CountB = 0; //起步时需要记为0,注意参数中的引用(&)

for (Z = 0; Z < KG; Z++)

{

F1= false; F2 = false; //开始寻找,暂时未找到任何前景点和背景点

Angle = (InitAngle + Z * IncAngle) / 180.0f * 3.1415926f; //每次搜索的角度

Dx = cos(Angle); Dy =sin(Angle);

Step= min(1.0f / (abs(Dx) + 1e-10), 1.0f / (abs(Dy) + 1e-10));

XD= X + 0.5; YD = Y + 0.5; //+0.5使用于int四舍五入取整,相当于其他语言的round函数

StepX = Step * Dx ; StepY = Step * Dy ; //StepX和StepY中必然有一个为1,另外一个小于1,这样保证每次搜索的像素点都会不同,一个好的建议是加大这个循环步长//这样有两个好处。1:加快查找速度;2:让搜索点可以进入已知点的内部,从而避免了只从已知点的边缘取样。但如果已知点的区域狭长,可能丢失有效取样点。

for(; ;)

{

XX= int(XD);if (XX < 0 || XX >= Width) break; //已经超出了图像的边界了,结束循环

YY = int(YD);if (YY < 0 || YY >= Height) break;

XD+= StepX; YD +=StepY;

Alpha= Mask[YY * MaskStride + XX]; //得到路径中该点的特征值(前景/背景/未知)

if (F1 == false && Alpha == 0) //如果沿这条路径尚未找到背景点,并且改点具有背景点的特征,则记录下改点到背景点序列中

{

B[CountB].X= XX; //背景点的X坐标

B[CountB].Y = YY; //背景点的Y坐标

CountB++; //背景点数量增加1

F1 = true; //在此路径已经找到了背景点,不用再找背景点了。

}else if (F2 == false && Alpha == 255) //如果沿这条路径尚未找到前景点,并且改点具有前景点的特征,则记录下改点到前景点序列中

{

F[CountF].X= XX; //前景点的X坐标

F[CountF].Y = YY; //前景点的X坐标

CountF++; //前景点数量增加1

F2 = true; //在此路径已经找到了前景点,不用再找前景点了。

}else if (F1 == true && F2 == true) //如果前景点和背景点都已经找到了,则结束循环

{break;

}

}

}

}

通过以上的Sample代码,我们就可以避免使用Vector之类的数据结构了,速度和内存占用都会得到改进。

然后在Gather阶段的代码改成如下的方式。

voidGathering()

{intX, Y, K, L, Index, Speed;intCountF, CountB, Fx, Fy, Bx, By;doublePfp, Min, Dpf, Gp;boolFlag;

Point*F = (Point *) malloc(KG * sizeof(Point)); //采用这种方式占用的内存小很多

Point *B = (Point *) malloc(KG * sizeof(Point));for (Y = 0; Y < Height; Y++)

{

Index= Y *MaskStride;for (X = 0; X < Width; X++)

{

tuple[Index].Flag= -1; //先都设置为无效的点

if (Mask[Index] != 0 && Mask[Index] != 255) //只处理未知点

{

Sample(X, Y, F, B, CountF, CountB);//对当前点进行前景和背景取样

Pfp = CalcPFP(X, Y, F, B, CountF, CountB); //计算公式(5),因为公式(5)只于前景和背景取样点的整体有关,无需放到下面的循环内部

Min =1e100;

Flag= false;for (K = 0; K < CountF; K++) //对于每一个前景点

{

Dpf= CalcDp(X, Y, F[K].X, F[K].Y, true); //计算前景点到中心点的欧式距离

for (L = 0; L < CountB; L++) //对于每一个背景点

{

Gp= CalcGp(X, Y, F[K].X, F[K].Y, B[L].X, B[L].Y, Pfp, Dpf); //按照公式(7)计算目标函数

if (Gp

{

Min=Gp;

Fx= F[K].X; Fy = F[K].Y; //记录下目标函数为最小值处的前景和背景点的坐标

Bx = B[L].X; By =B[L].Y;

Flag= true;

}

}

}if (Flag == true) //说明找到了最好的组合了,如果找不到,则原因可能是:(1)Sample过程为找到任何有效的前景和背景点;(2)Sample过程只找到前景点或只找到背景点

{ //某个点找不到也不用怕,可能在下面的Refine过程中(其算法为领域处理)得以弥补。

Speed = Fy * Stride + Fx * 3; //记录下最佳前景点的颜色值

tuple[Index].BF =ImageData[Speed];

tuple[Index].GF= ImageData[Speed + 1];

tuple[Index].RF= ImageData[Speed + 2];

Speed= By * Stride + Bx * 3;

tuple[Index].BB= ImageData[Speed]; //记录下最佳背景点的颜色值

tuple[Index].GB = ImageData[Speed + 1];

tuple[Index].RB= ImageData[Speed + 2];

tuple[Index].SigmaF= CaclSigma(Fx, Fy); //计算前景点周边像素的均方差值

tuple[Index].SigmaB = CaclSigma(Bx, By); //计算背景点周边像素的均方差值

tuple[Index].Flag = 1; //这个像素是个有效的处理点

}

}

Index++;

}

}

free(F);

free(B);

}

由于CSDN提供了代码,其他的代码我这里就不提供了。

五、算法的缺陷

以下内容纯属个人意见,请各位阅读的朋友根据自己的知识去判断。

1、Sample过程存在潜在的问题:论文的图2阐述了对某点进行取样的操作过程,这个过程在第一次遇到前景或背景点时就把该点视为前景或背景的一个取样点。这样的话所有的取样点的数据都只可能取在Trimap或者scripple图的前景和背景区域的边缘处。如果边缘处的像素和内部的有很大的区别,显然会出现部分取样点取到的数据很不合理,从而导致最终的透明度信息错误。一种可行的方案就是在Sample的过程中修改每次取样的步长,比如,下面的方式:

StepX = Step * Dx * 10 ; StepY = Step * Dy * 10 ;

即每次沿X或者Y方向移动10个像素,这样就会使得取样点会进入已知区域内部最多10个像素左右,同时也会加速取样的速度。

不过这样做也隐形的带来一个新的问题,即有可能会丢失一些取样点,当已知点的宽度小于10像素时出现这种情况。

2、还是会存在某些点无法获取有效的取样点的,即使有了refine过程。

3、算法的复杂度还是很高,虽然整体的并行性很好,如果没有GPU参与,纯CPU实现起来一样很吃力。

六、效果验证

*********************************作者: laviewpbt   时间: 2014.2.15   联系QQ:  33184777  转载请保留本行信息************************

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

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

相关文章

女生做产品经理好吗_女生天生就是产品经理,不服来战!

关注&#x1f51d;蓝字&#xff0c;获取求职干货信息大家好&#xff0c;姗姗来迟~这个梗会不会被扣工资前两天&#xff0c;小米的资深产品经理Alina老师征集用户需求&#xff1a;关于产品经理的直播课&#xff0c;大家想听什么呀&#xff1f;姗姗一直很好奇&#xff1a;一个对逻…

c语言文件读写r 的作用,C语言 读写二进制文件

查找了比较多的资源&#xff0c; 发现没有办法把text 文件转成binary文件仅作为记录&#xff0c;不过这个例子可以去除换行符。#include #include #define N 255int main(){char a[N];FILE *fp1,*fp2;fp1fopen("test_seq.fa","r");fp2fopen("testSeq.…

verlay虚拟化技术_FPGA虚拟化:突破次元壁的技术

原标题&#xff1a;FPGA虚拟化&#xff1a;突破次元壁的技术来源&#xff1a;内容来自「老石谈芯」&#xff0c;作者 老石&#xff0c;谢谢。1利用FPGA虚拟化突破时空限制在传统的FPGA开发模型中&#xff0c;使用者通常使用硬件描述语言(HDL)对应用场景进行建模&#xff0c;然后…

tcp报文格式_34.TCP取样器

阅读文本大概需要3分钟。1、TCP取样器的作用TCP取样器作用就是通过TCP/IP协议来连接服务器&#xff0c;然后发送数据和接收数据。2、TCP取样器详解TCPClient classname&#xff1a;TCP报文格式类名&#xff0c;默认前缀org.apache.jmeter.protocol.tcp.sampler.① TCPClientImp…

c语言程序设计字符处理周信东,“电子科技大学出版社(周信东主编)”的C语言程序设计实验-整理代码-.doc...

“电子科技大学出版社(周信东主编)”的C语言程序设计实验-整理代码-.doc-前言-/*非常感谢度?娘以及各位?网上C语言?高手的支持?&#xff0c;才能让敝人?完成此文档?的整理。本文档集合?了本人、度娘、众网友的力?量&#xff0c;其中代码的?正确率约为?90%(不正确的有…

pythonwx功能_python中wx模块的具体使用方法

wx包中的方法都是以大写字母开头的&#xff0c;而这和Python的习惯是相反的。 本节介绍如何创建python程序的图形用户界面&#xff08;GUI&#xff09;&#xff0c;也就是那些带有按钮和文本框的窗口。这里介绍wxPython &#xff1a; 根据自己的操作系统下载相应版本。 安装&am…

c语言函数的使用步骤,c语言打开文件函数使用方法

ANSI C规定文件打开用函数fopen&#xff0c;关闭为fclose。1、调用方式通常为:FILE *fp;fpfopen(文件名, 打开方式);2、参数说明:文件名: 形如"myfile.dat"、"F:\data\myfile.dat"等等;打开方式:"r"(只读) 为输入打开一个文本文件"w"(…

fpga如何约束走线_FPGA时序约束实战篇之多周期路径约束

多周期路径约束多周期路径&#xff0c;我们一般按照以下4个步骤来约束&#xff1a;1. 带有使能的数据首先来看带有使能的数据&#xff0c;在本工程中的Tming Report中&#xff0c;也提示了同一个时钟域之间的几个路径建立时间不满足要求其实这几个路径都是带有使能的路径&#…

uml 类图_UML-类图

概念Class diagram is UML structure diagram which shows structure of the designed system at the level of classes and interfaces, shows their features, constraints and relationships - associations, generalizations, dependencies, etc.类图是用于描述类、接口这一…

牙齿间隙变大怎么办_牙齿之间的间隙越来越大怎么办?

无论我们是否发现牙齿与牙齿之间有很大的裂缝问题&#xff0c;这不仅影响到面部的美观&#xff0c;而且对正常的饮食、咀嚼疲劳也有一定的影响。随着我国生活条件的改善&#xff0c;人们对生活质量的要求也越来越高&#xff0c;同时也开始关注自己的口腔状况&#xff0c;牙齿问…

python读取文件中的内容_python 读取文件夹中的文件内容

看thinking in java的时候发现有个题的答案不确定结果&#xff0c; 于是下载答案看下&#xff0c;结果是这个样子的,这样要怎么才能找到相对应的答案&#xff1f;于是我就着手写了一个快速遍历的脚本&#xff08;我这里只是单纯的找了出来&#xff0c; 没有把找到的文件单独拿出…

C语言编程日志,用C语言打印日志(Log)

用C语言打印日志(Log)直接上源代码。log.h 文件&#xff1a;/** log.h **/#ifndef __LOG_H__#define __LOG_H__#include "stdio.h"#include "string.h"#include "stdlib.h"#include "time.h"#include "stdarg.h"#include &q…

binlog数据库不写入binlog_京东智联云MySQL数据库如何保障数据的可靠性?

MySQL作为当前最流行的关系型数据库&#xff0c;在各个行业的系统中扮演着最重要的角色。随着大家对数据价值认可的逐步加深&#xff0c;数据的可靠性是最常被问到的一个问题。MySQL是如何保证数据可靠性的&#xff1f;京东智联云RDS-MySQL又做了哪些优化和新特性来保证用户数据…

在职高学C语言程序设计,中职学校C语言程序设计教学方法.doc

中职学校C语言程序设计教学方法中职学校C语言程序设计的教学方法摘 要&#xff1a;计算机专业中&#xff0c;C语言是一门基础的程序设计课&#xff0c;但学习《C语言程序设计》相对职高学生来说难度较大&#xff0c;但它却是很实用的一门课程&#xff0c;同时又是我省计算机对口…

js和python哪个好_Python与Node.JS:哪一个比较适合您的项目?

在进行新项目时选择正确的编程语言可能是程序员经常做出的比较艰巨的决定之一。这个挑战背后的原因是&#xff0c;每个新项目都会遇到一个独特的问题&#xff0c;并且在编程世界中&#xff0c;没有任何行业的大师。 不同的编程语言都有其长处和短处&#xff0c;这使其适用于某些…

typescript的类型描述_一文学懂TypeScript的类型

你将学到什么阅读本文后&#xff0c;你应该能够理解以下代码的含义&#xff1a;interface Array{concat(...items: Array): T[];reduce(callback: (state: U, element: T, index: number, array: T[]) >U,firstState?: U): U;}如果你认为这段代码非常神秘 —— 那么我同意你…

equation在c语言中是什么意思,MathType出现此对象创建于Equation中的问题怎么办

使用MathType出错的窗口&#xff1a;MathType程序停止工作提醒窗口&#xff1a;解决方法如下&#xff1a;1.打开Word文件&#xff0c;在Word菜单中选择“工具”——“模板和加载项”&#xff0c;将会跳出一个模板和加载项的对话框。在Word菜单中选择“工具”——“模板和加载项…

python messagebox弹窗退出_python 弹窗提示警告框MessageBox的实例

需要安装pywin32模块&#xff0c;pip install pywin32 ##pip install pywin32 import win32api,win32con ##提醒OK消息框 win32api.MessageBox(0, "这是一个测试提醒OK消息框", "提醒",win32con.MB_OK) ##是否信息框 win32api.MessageBox(0, "这是一个…

gradle是否可以编译c语言,build.gradle按条件编译与cmake配置

在build.gradle里面通过productFlavors就可以方便的实现不同的编译方案。flavorDimensions定义维度flavorDimensions 从单词字面理解知道是 “风味维度”&#xff0c;是需要结合 “产品风味(即productFlavors)” 来一起使用的。flavorDimensions 的使用会定义出维度&#xff0c…

post请求改成body_post请求body格式

在PostMan中用Post方式&#xff0c;Body有form-data,x-www-form-urlencoded,raw,binary四种。其中raw又分以下7种。现在来区分一下&#xff1a;form-data是http请求中的multipart/form-data,它会将表单的数据处理为一条消息&#xff0c;以标签为单元&#xff0c;用分隔符分开。…