zoukankan      html  css  js  c++  java
  • 遗留物检测算法及实现

    算法一:

    《An abandoned object detection system based on dual backgroundsegmentation》 IEEE 2009

    搞了两个背景缓冲区:

    Current_background:初始为第一帧,其后对每个像素,若下一帧像素大于该背景像素,则该出背景像素加1,否则该处背景像素减1。【挺神奇的,好处是This way, even if the foreground ischanging at a fast pace, it will not affect the background but if theforeground is stationary, it gradually merges into the background.但是效果还是不及混合高斯,因为适应期太长太频繁了】

     Buffer_background:论文上说每隔20秒更新一次,直接拷贝Current_background,遗留物检测直接通过Current_background和Buffer_background相减即可。【他这边认为一个遗留物丢弃满20秒,如果还在,则认为该遗留物为背景了】。随后,搞了一堆跟踪该区域的东西,我不太感兴趣。

     本文对上述的改进的目标是物体如果被遗弃了,那么它就应该一直被检测到。我额外搞了一个遗弃背景模版abandon_background,用于记录遗弃物之前的背景图像。

    A.若物体离开,则abandon相应区域恢复到当前current的值,buffer更新为当前整个current。B.若物体未离开,则用abandon对buffer局部更新

    整体算法:

    1.      第一帧,用来初始化Current_background和Buffer_background

    2.      通过两个背景区域计算遗留物

    3.      每一帧更新Current_background

    4.      若时间间隔满足,更新Buffer_background和遗弃物背景abandon_background,更新计数器

       a.      若遗弃物背景首次更新,根据current和buffer之差,在相应的地方赋current的值,其余为0

       b.      进行物体离开判断,即:若abandon与current对应区域背景像素不同,则物体还未离开,否则离开

       c.      若物体离开,将current更新abandon的相应区域,buffer复制完整的current

       d.      若物体未离开,将abandon更新buffer的相应区域,abandon保持不变。

    5.      读取下一帧,返回2

    代码:

    调用的函数:

    1. #include "Model.h"  
    2. #define Th 50  
    3. #define Ta 90  
    4.   
    5. int first_update = 0;//首次更新标志  
    6.   
    7. void calc_fore(IplImage *current,IplImage *back,IplImage *fore)  
    8. {  
    9.     int i,j;  
    10.   
    11.     for (i=0;i<current->height;i++)  
    12.     {  
    13.         for (j=0;j<current->width;j++)  
    14.         {  
    15.             if (abs((u_char)current->imageData[i*current->widthStep+j] - (u_char)back->imageData[i*current->widthStep+j]) <=Ta )  
    16.             {  
    17.                 fore->imageData[i*current->widthStep+j] = 0;//background  
    18.             }else  
    19.             {  
    20.                 fore->imageData[i*current->widthStep+j] = 255;//foreground  
    21.             }  
    22.         }  
    23.     }  
    24. }  
    25.   
    26. void update_currentback(IplImage *current,IplImage *curr_back)  
    27. {  
    28.     int i,j;  
    29.     int p,q;  
    30.   
    31.     for (i=0;i<current->height;i++)  
    32.     {  
    33.         for (j=0;j<current->width;j++)  
    34.         {  
    35.             p = (u_char)current->imageData[i * current->widthStep + j];  
    36.             q = (u_char)curr_back->imageData[i * current->widthStep + j];  
    37.             //printf("%d,%d ",p,q);  
    38.   
    39.             if (p >= q)  
    40.             {  
    41.                 if (q == 255)  
    42.                 {  
    43.                     q = 254;  
    44.                 }  
    45.                 curr_back->imageData[i * current->widthStep + j] = q + 1;  
    46.             }else  
    47.             {  
    48.                 if (q == 0)  
    49.                 {  
    50.                     q = 1;  
    51.                 }  
    52.                 curr_back->imageData[i * current->widthStep + j] = q - 1;  
    53.             }  
    54.         }  
    55.     }  
    56. }  
    57.   
    58. void update_bufferedback(IplImage *curr_back,IplImage *buf_back,IplImage *abandon)  
    59. {  
    60.     int i,j,height,width;  
    61.     int leave_flag = 0;  
    62.       
    63.     height = curr_back->height;  
    64.     width = curr_back->widthStep;  
    65.   
    66.     if (first_update == 0)  
    67.     {  
    68.         for ( i = 0;i < height; i++)  
    69.         {  
    70.             for (j = 0;j < width; j++)  
    71.             {  
    72.                 if (abs((u_char)curr_back->imageData[i*width+j] - (u_char)buf_back->imageData[i*width+j]) <=Th )  
    73.                 {  
    74.                     abandon->imageData[i*width+j] = 0;//background  
    75.                 }else  
    76.                 {  
    77.                     abandon->imageData[i*width+j] = curr_back->imageData[i*width+j];//foreground  
    78.                     first_update = 1;  
    79.                 }  
    80.             }  
    81.         }  
    82.         return;  
    83.     }  
    84.   
    85.     //物体离开判断  
    86.     for ( i = 0;i < height; i++)  
    87.     {  
    88.         for (j = 0;j < width; j++)  
    89.         {  
    90.             if (abandon->imageData[i*width+j] != 0)  
    91.             {  
    92.                 if (abandon->imageData[i*width+j] != curr_back->imageData[i*width+j])  
    93.                 {  
    94.                     leave_flag = 1;                 //物体掩膜处之前背景与当前的不一致,1:物体未离开,0:物体离开  
    95.                 }  
    96.             }  
    97.         }  
    98.     }  
    99.       
    100.     if(leave_flag == 0)     //物体离开  
    101.     {  
    102.         cvCopy(curr_back,buf_back);  
    103.         for ( i = 0;i < height; i++)  
    104.         {  
    105.             for (j = 0;j < width; j++)  
    106.             {  
    107.                 if (abandon->imageData[i*width+j] != 0)  
    108.                 {  
    109.                     abandon->imageData[i*width+j] = curr_back->imageData[i*width+j];  
    110.                 }  
    111.             }  
    112.         }  
    113.     }else  
    114.     {  
    115.         for ( i = 0;i < height; i++)  
    116.         {  
    117.             for (j = 0;j < width; j++)  
    118.             {  
    119.                 if (abandon->imageData[i*width+j] != 0)  
    120.                 {  
    121.                     buf_back->imageData[i*width+j] = abandon->imageData[i*width+j];  
    122.                 }  
    123.             }  
    124.         }  
    125.     }  
    126.       
    127.   
    128. }  


    主函数:

    1. // abandon_left.cpp : 定义控制台应用程序的入口点。  
    2. //  
    3.   
    4. #include "stdafx.h"  
    5. #include "Model.h"  
    6.   
    7. int _tmain(int argc, _TCHAR* argv[])  
    8. {  
    9.     CvCapture *capture=cvCreateFileCapture("test.avi");  
    10.     IplImage *current_back,*buff_back,*abandon,*frame,*current_img,*fore;  
    11.     int count,intern;  
    12.   
    13.     frame = cvQueryFrame(capture);  
    14.     fore = cvCreateImage(cvSize(frame->width,frame->height),IPL_DEPTH_8U,1);  
    15.     current_back = cvCreateImage(cvSize(frame->width,frame->height),IPL_DEPTH_8U,1);  
    16.     current_img = cvCreateImage(cvSize(frame->width,frame->height),IPL_DEPTH_8U,1);  
    17.     buff_back = cvCreateImage(cvSize(frame->width,frame->height),IPL_DEPTH_8U,1);  
    18.     abandon = cvCreateImage(cvSize(frame->width,frame->height),IPL_DEPTH_8U,1);  
    19.   
    20.     count=0;  
    21.     intern = count + 20;  
    22.       
    23.     while (1)  
    24.     {  
    25.         cvCvtColor(frame,current_img,CV_RGB2GRAY);  
    26.   
    27.         if (count == 0)  
    28.         {  
    29.             //初始化背景模版  
    30.             cvCopy(current_img,current_back);  
    31.             cvCopy(current_img,buff_back);  
    32.         }  
    33.   
    34.         if (count > 0)  
    35.         {  
    36.             //计算前景掩膜  
    37.             calc_fore(current_back,buff_back,fore);  
    38.   
    39.             //更新跟踪背景  
    40.             update_currentback(current_img,current_back);  
    41.   
    42.             if (count == intern)  
    43.             {  
    44.                 update_bufferedback(current_back,buff_back,abandon);  
    45.                 intern = count + 20;  
    46.             }  
    47.             cvShowImage("current",current_img);  
    48.             cvShowImage("current_back",current_back);  
    49.             cvShowImage("buff_back",buff_back);  
    50.             cvShowImage("abandon detection",fore);  
    51.         }  
    52.   
    53.         count++;  
    54.         frame =cvQueryFrame(capture);  
    55.   
    56.         if (cvWaitKey(23)>=0)  
    57.         {  
    58.             break;  
    59.         }  
    60.     }  
    61.     cvNamedWindow("current",0);  
    62.     cvNamedWindow("buff_back",0);  
    63.     cvNamedWindow("current_back",0);  
    64.     cvNamedWindow("abandon detection",0);  
    65.     cvReleaseCapture(&capture);  
    66.     return 0;  
    67. }  

    效果图:

    说明:左下角有个女的运动规律也符合静止目标检测规律,所以也被检测出来了。后期可以通过外接矩形长宽比等其他手段过滤掉。

    视频+代码工程的下载连接:http://download.csdn.net/detail/jinshengtao/7157943

    算法二:

    《一种基于双背景模型的遗留物检测方法》

    搞了个脏背景和纯背景,定义:

    当视频场景中不出现运动目标,或者背景不受场景中所出现的运动目标影响时,这样的背景称为纯背景。否则,称为脏背景

    它们的更新规则:

    一般背景的更新按照帧间差分法:

    脏背景使用全局更新,直接赋值一般背景:

    纯背景根据前景掩膜,进行局部更新,即若前景掩膜被标记为运动的部分,则相应的纯背景区域用上一帧的纯背景更新;若前景掩膜被标记为非运动的部分,则相应的纯背景区域用当前帧的一般背景更新。

    静止目标前景检测算法可以通过以下公式看明白:

    具体算法流程不给咯,论文没提,自己摸索的,反正试验效果失败了。

    代码:

    1. // left_bag.cpp : 定义控制台应用程序的入口点。  
    2. //  
    3.   
    4. #include "stdafx.h"  
    5. #include "cv.h"  
    6. #include "highgui.h"  
    7. #define u_char unsigned char  
    8. #define alfa 0.03  
    9. #define Th 60  
    10. #define Ta 60  
    11. #define Tb 40  
    12.   
    13. void calc_fore(IplImage *current,IplImage *back,IplImage *fore)  
    14. {  
    15.     int i,j;  
    16.   
    17.     for (i=0;i<current->height;i++)  
    18.     {  
    19.         for (j=0;j<current->width;j++)  
    20.         {  
    21.             if (abs((u_char)current->imageData[i*current->widthStep+j] - (u_char)back->imageData[i*current->widthStep+j]) <=Th )  
    22.             {  
    23.                 fore->imageData[i*current->widthStep+j] = 0;//background  
    24.             }else  
    25.             {  
    26.                 fore->imageData[i*current->widthStep+j] = 255;//foreground  
    27.             }  
    28.         }  
    29.     }  
    30. }  
    31.   
    32. void update_back(IplImage *current,IplImage *back,IplImage *B_p,IplImage *B_d,IplImage *fore,IplImage *B_p_pre)  
    33. {  
    34.     int i,j;  
    35.   
    36.     //更新B_n  
    37.     for (i=0;i<current->height;i++)  
    38.     {  
    39.         for (j=0;j<current->width;j++)  
    40.         {  
    41.             back->imageData[i*current->widthStep+j] = (1-alfa)*current->imageData[i*current->widthStep+j] + alfa * back->imageData[i*current->widthStep+j];         
    42.         }  
    43.     }  
    44.   
    45.     //更新B_d  
    46.     cvCopy(back,B_d);  
    47.       
    48.     //更新B_p  
    49.     for (i = 0;i < fore->height;i++)  
    50.     {  
    51.         for (j = 0;j < fore->width;j++)  
    52.         {  
    53.             if ((unsigned char)fore->imageData[i*fore->widthStep + j ] == 255)  
    54.             {  
    55.                 B_p->imageData[i*fore->widthStep + j] = B_p_pre->imageData[i*fore->widthStep + j];  
    56.             }else  
    57.             {  
    58.                 B_p->imageData[i*fore->widthStep + j] = back->imageData[i*fore->widthStep + j];  
    59.             }  
    60.         }  
    61.     }  
    62. }  
    63.   
    64. void calc_StaticTarget(IplImage *current,IplImage *B_d,IplImage *B_p,IplImage *M_s,IplImage *M_m,IplImage *M_f)  
    65. {  
    66.     int i,j;  
    67.   
    68.     for (i = 0;i < current->height;i++)  
    69.     {  
    70.         for (j = 0;j < current->width;j++)  
    71.         {  
    72.             if (abs((u_char)current->imageData[i*current->widthStep+j] - (u_char)B_d->imageData[i*current->widthStep+j]) <= Th )  
    73.             {  
    74.                 M_s->imageData[i*current->widthStep+j] = 255;  
    75.             }else  
    76.             {  
    77.                 M_s->imageData[i*current->widthStep+j] = 0;  
    78.             }  
    79.   
    80.             if (abs((u_char)B_p->imageData[i*current->widthStep+j] - (u_char)B_d->imageData[i*current->widthStep+j]) > Tb)  
    81.             {  
    82.                 M_m->imageData[i*current->widthStep+j] = 255;  
    83.             }else  
    84.             {  
    85.                 M_m->imageData[i*current->widthStep+j] = 0;  
    86.             }  
    87.   
    88.             if (((unsigned char)M_m->imageData[i*current->widthStep+j] == 255) &&((unsigned char)M_s->imageData[i*current->widthStep+j] == 255))  
    89.             {  
    90.                 M_f->imageData[i*current->widthStep+j] = 255;  
    91.             }else  
    92.             {  
    93.                 M_f->imageData[i*current->widthStep+j] = 0;  
    94.             }  
    95.         }  
    96.     }  
    97. }  
    98.   
    99. int _tmain(int argc, _TCHAR* argv[])  
    100. {  
    101.     CvCapture *capture=cvCreateFileCapture("test.avi");  
    102.     IplImage *frame,*current_img,*B_n,*B_p,*B_d,*B_p_pre;  
    103.     IplImage *M,*M1,*M_s,*M_m,*M_f;  
    104.     int count,i,j;  
    105.   
    106.     frame = cvQueryFrame(capture);  
    107.     current_img = cvCreateImage(cvSize(frame->width,frame->height),IPL_DEPTH_8U,1);  
    108.     B_n = cvCreateImage(cvSize(frame->width,frame->height),IPL_DEPTH_8U,1);  
    109.     B_p = cvCreateImage(cvSize(frame->width,frame->height),IPL_DEPTH_8U,1);  
    110.     B_d = cvCreateImage(cvSize(frame->width,frame->height),IPL_DEPTH_8U,1);  
    111.     B_p_pre = cvCreateImage(cvSize(frame->width,frame->height),IPL_DEPTH_8U,1);  
    112.   
    113.     M = cvCreateImage(cvSize(frame->width,frame->height),IPL_DEPTH_8U,1);  
    114.     M1 = cvCreateImage(cvSize(frame->width,frame->height),IPL_DEPTH_8U,1);  
    115.     M_s = cvCreateImage(cvSize(frame->width,frame->height),IPL_DEPTH_8U,1);  
    116.     M_m = cvCreateImage(cvSize(frame->width,frame->height),IPL_DEPTH_8U,1);  
    117.     M_f = cvCreateImage(cvSize(frame->width,frame->height),IPL_DEPTH_8U,1);  
    118.   
    119.     count=0;  
    120.     while (1)  
    121.     {  
    122.         cvCvtColor(frame,current_img,CV_RGB2GRAY);  
    123.   
    124.         if (count == 0)  
    125.         {  
    126.             //初始化各种背景模版  
    127.             cvCopy(current_img,B_n);  
    128.             cvCopy(current_img,B_p);  
    129.             cvCopy(current_img,B_d);  
    130.             cvCopy(current_img,B_p_pre);  
    131.         }  
    132.   
    133.         if (count > 1)  
    134.         {  
    135.             //计算前景掩膜  
    136.             calc_fore(current_img,B_n,M1);  
    137.   
    138.             //膨胀腐蚀操作  
    139.             cvDilate(M1, M, 0, 1);  
    140.             cvErode(M, M1, 0, 2);  
    141.             cvDilate(M1, M, 0,1);     
    142.   
    143.             //静止目标检测  
    144.             calc_StaticTarget(current_img,B_d,B_p,M_s,M_m,M_f);  
    145.   
    146.             //更新跟踪背景  
    147.             update_back(current_img,B_n,B_p,B_d,M,B_p_pre);  
    148.   
    149.             cvShowImage("pure ground",B_p);  
    150.             cvShowImage("dirty ground",B_d);  
    151.             cvShowImage("static target",M_f);  
    152.             cvShowImage("fore ground",M);  
    153.             cvCopy(B_p,B_p_pre);  
    154.         }  
    155.   
    156.         count++;  
    157.         frame =cvQueryFrame(capture);  
    158.           
    159.   
    160.         if (cvWaitKey(23)>=0)  
    161.         {  
    162.             break;  
    163.         }  
    164.     }  
    165.     cvNamedWindow("pure ground",0);  
    166.     cvNamedWindow("dirty ground",0);  
    167.     cvNamedWindow("static target",0);  
    168.     cvNamedWindow("fore ground",0);  
    169.     cvReleaseCapture(&capture);  
    170.     return 0;  
    171. }  


    算法三【MATLAB toolbox中的一个demo】

    理论部分没看,在控制台直接输入:edit videoabandonedobj 会有相应的代码跳出来。

    在help中搜索Abandoned Object Detection,会有理论部分介绍

    视频素材下载地址:http://www.mathworks.cn/products/viprocessing/vipdemos.html

    代码:【2010b 版本可跑】

    1. clc;  
    2. clear;  
    3.   
    4. status = videogetdemodata('viptrain.avi');  
    5. if ~status  
    6.     displayEndOfDemoMessage(mfilename);  
    7.     return;  
    8. end  
    9.   
    10. roi = [80 100 240 360];  
    11. % Maximum number of objects to track  
    12. maxNumObj = 200;  
    13. % Number of frames that an object must remain stationary before an alarm is  
    14. % raised  
    15. alarmCount = 45;  
    16. % Maximum number of frames that an abandoned object can be hidden before it  
    17. % is no longer tracked  
    18. maxConsecutiveMiss = 4;  
    19. % Maximum allowable change in object area in percent  
    20. areaChangeFraction = 15;  
    21. % Maximum allowable change in object centroid in percent  
    22. centroidChangeFraction = 20;  
    23. % Minimum ratio between the number of frames in which an object is detected  
    24. % and the total number of frames, for that object to be tracked.  
    25. minPersistenceRatio = 0.7;  
    26. % Offsets for drawing bounding boxes in original input video  
    27. PtsOffset = int32(repmat([roi(1); roi(2); 0 ; 0],[1 maxNumObj]));  
    28.   
    29. hVideoSrc = video.MultimediaFileReader;  
    30. hVideoSrc.Filename = 'viptrain.avi';  
    31. hVideoSrc.VideoOutputDataType = 'single';  
    32.   
    33. hColorConv = video.ColorSpaceConverter;  
    34. hColorConv.Conversion = 'RGB to YCbCr';  
    35.   
    36. hAutothreshold = video.Autothresholder;  
    37. hAutothreshold.ThresholdScaleFactor = 1.3;  
    38.   
    39. hClosing = video.MorphologicalClose;  
    40. hClosing.Neighborhood = strel('square',5);  
    41.   
    42. hBlob = video.BlobAnalysis;  
    43. hBlob.MaximumCount = maxNumObj;  
    44. hBlob.NumBlobsOutputPort = true;  
    45. hBlob.MinimumBlobAreaSource = 'Property';  
    46. hBlob.MinimumBlobArea = 100;  
    47. hBlob.MaximumBlobAreaSource = 'Property';  
    48. hBlob.MaximumBlobArea = 2500;  
    49. hBlob.ExcludeBorderBlobs = true;  
    50.   
    51. hDrawRectangles1 = video.ShapeInserter;  
    52. hDrawRectangles1.Fill = true;  
    53. hDrawRectangles1.FillColor = 'Custom';  
    54. hDrawRectangles1.CustomFillColor = [1 0 0];  
    55. hDrawRectangles1.Opacity = 0.5;  
    56.   
    57. hDisplayCount = video.TextInserter;  
    58. hDisplayCount.Text = '%4d';  
    59. hDisplayCount.Color = [1 1 1];  
    60.   
    61. hAbandonedObjects = video.VideoPlayer;  
    62. hAbandonedObjects.Name = 'Abandoned Objects';  
    63. hAbandonedObjects.Position = [10 300 roi(4)+25 roi(3)+25];  
    64.   
    65. hDrawRectangles2 = video.ShapeInserter;  
    66. hDrawRectangles2.BorderColor = 'Custom';  
    67. hDrawRectangles2.CustomBorderColor = [0 1 0];  
    68.   
    69. hDrawBBox = video.ShapeInserter;  
    70. hDrawBBox.BorderColor = 'Custom';  
    71. hDrawBBox.CustomBorderColor = [1 1 0];  
    72.   
    73. hAllObjects = video.VideoPlayer;  
    74. hAllObjects.Position = [45+roi(4) 300 roi(4)+25 roi(3)+25];  
    75. hAllObjects.Name = 'All Objects';  
    76.   
    77. hDrawRectangles3 = video.ShapeInserter;  
    78. hDrawRectangles3.BorderColor = 'Custom';  
    79. hDrawRectangles3.CustomBorderColor = [0 1 0];  
    80.   
    81. hThresholdDisplay = video.VideoPlayer;  
    82. hThresholdDisplay.Position = ...  
    83.             [80+2*roi(4) 300 roi(4)-roi(2)+25 roi(3)-roi(1)+25];  
    84. hThresholdDisplay.Name = 'Threshold';  
    85.   
    86. firsttime = true;  
    87. while ~isDone(hVideoSrc)  
    88.     Im = step(hVideoSrc);  
    89.   
    90.     % Select the region of interest from the original video  
    91.     OutIm = Im(roi(1):end, roi(2):end, :);  
    92.   
    93.     YCbCr = step(hColorConv, OutIm);  
    94.     CbCr  = complex(YCbCr(:,:,2), YCbCr(:,:,3));  
    95.   
    96.     % Store the first video frame as the background  
    97.     if firsttime  
    98.         firsttime = false;  
    99.         BkgY      = YCbCr(:,:,1);  
    100.         BkgCbCr   = CbCr;  
    101.     end  
    102.     SegY    = step(hAutothreshold, abs(YCbCr(:,:,1)-BkgY));  
    103.     SegCbCr = abs(CbCr-BkgCbCr) > 0.05;  
    104.   
    105.     % Fill in small gaps in the detected objects  
    106.     Segmented = step(hClosing, SegY | SegCbCr);  
    107.   
    108.     % Perform blob analysis  
    109.     [Area, Centroid, BBox, Count] = step(hBlob, Segmented);  
    110.   
    111.     % Call the helper function that tracks the identified objects and  
    112.     % returns the bounding boxes and the number of the abandoned objects.  
    113.     [OutCount, OutBBox] = videoobjtracker(Area, Centroid, BBox, Count,...  
    114.        areaChangeFraction, centroidChangeFraction, maxConsecutiveMiss, ...  
    115.        minPersistenceRatio, alarmCount);  
    116.   
    117.     % Display the abandoned object detection results  
    118.     Imr = step(hDrawRectangles1, Im, OutBBox+PtsOffset);  
    119.     Imr(1:15,1:30,:) = 0;  
    120.     Imr = step(hDisplayCount, Imr, OutCount);  
    121.     step(hAbandonedObjects, Imr);  
    122.   
    123.     % Display all the detected objects  
    124.     Imr = step(hDrawRectangles2, Im, BBox+PtsOffset);  
    125.     Imr(1:15,1:30,:) = 0;  
    126.     Imr = step(hDisplayCount, Imr, OutCount);  
    127.     Imr = step(hDrawBBox, Imr, roi);  
    128.     step(hAllObjects, Imr);  
    129.   
    130.     % Display the segmented video  
    131.     SegIm = step(hDrawRectangles3, repmat(Segmented,[1 1 3]), BBox);  
    132.     step(hThresholdDisplay, SegIm);  
    133. end  
    134.   
    135. release(hVideoSrc);  
  • 相关阅读:
    JavaScript数组升降序排列、最大值、最小值等
    css3箭头
    隐藏显示
    最后一个 last-of-type
    jquery函数封装
    为什么要使用rem
    Git的使用--如何将本地项目上传到Github
    jQuery判断是否选中
    数组索引赋值
    HTML中input和button设置同样高度却不能等高的原因
  • 原文地址:https://www.cnblogs.com/ywsoftware/p/4434607.html
Copyright © 2011-2022 走看看