2013-05-11 11:10:22 qianmianyuan 阅读数 2726
  • 1.14.ARM裸机第十四部分-LCD显示器

    本期课程主要讲解LCD。首先讲了LCD显示原理、颜色原理、LCD显示器的主要性能参数、概念等,然后进入了LCD编程实战部分,用6节课从零开始带大家编写程序在LCD上显示像素、背景、线条、文字、图片等内容。本课程的学习目标是掌握LCD显示相关的概念和编程方法,对显存的作用和填充有本质的了解,对图像显示有一定的认识。

    8393 人正在学习 去看看 朱有鹏

padarray
功能:填充图像或填充数组。
用法:B = padarray(A,padsize,padval,direction)
      A
为输入图像,B为填充后的图像,

padsize给出了给出了填充的行数和列数,通常用[r c]来表示。

padval表示填充方法。它的具体值和描述如下:
  padval:      'symmetric'表示图像大小通过围绕边界进行镜像反射来扩展;
                          'replicate'
表示图像大小通过复制外边界中的值来扩展;
                          'circular'
图像大小通过将图像看成是一个二维周期函数的一个周期来进行扩展。

 

direction表示填充的方向。它的具体值和描述如下:
   direction 
:   'pre'表示在每一维的第一个元素前填充;
                        'post'
表示在每一维的最后一个元素后填充;
                        'both'
表示在每一维的第一个元素前和最后一个元素后填充,此项为默认值。


若参量中不包括direction,则默认值为'both'

若参量中不包含padval,则默认用零来填充。

若参量中不包括任何参数,则默认填充为零且方向为'both'。在计算结束时,图像会被修剪成原始大小。

2008-09-03 23:20:00 maozefa 阅读数 7456
  • 1.14.ARM裸机第十四部分-LCD显示器

    本期课程主要讲解LCD。首先讲了LCD显示原理、颜色原理、LCD显示器的主要性能参数、概念等,然后进入了LCD编程实战部分,用6节课从零开始带大家编写程序在LCD上显示像素、背景、线条、文字、图片等内容。本课程的学习目标是掌握LCD显示相关的概念和编程方法,对显存的作用和填充有本质的了解,对图像显示有一定的认识。

    8393 人正在学习 去看看 朱有鹏

阅读提示:

    《Delphi图像处理》系列以效率为侧重点,一般代码为PASCAL,核心代码采用BASM。

    《C++图像处理》系列以代码清晰,可读性为主,全部使用C++代码。

    尽可能保持二者内容一致,可相互对照。

    本文代码必须包括文章《Delphi图像处理 -- 数据类型及公用过程》中的ImageData.pas单元   

 

    这个填充浮雕效果过程代码已经完成好几天了,但是一直没敢在BLOG上发表,因为这是我在研究Photoshop浮雕效果做实验时,无意中写的一段代码,当时就感觉这个效果虽然不是我想要的Photoshop浮雕效果,但比Photoshop浮雕效果的应用价值应该不会差,可以说是各有特色。只是当时感觉处理速度较慢,同时顾虑该浮雕效果是我妙想天开,发表后会不会有引起嘲笑。经过了几天的改进和原理论证,我觉得该浮雕效果从原理上是说得通的,而改进后的速度一般情况下比彩色浮雕处理快,在浮雕深度较小(< 8)的时候,比灰色浮雕处理还快,所以还是决定发表在这里,供大家讨论其实用性,或给出改进意见。至于这种浮雕效果的名称,刚开始时因其效果类似石雕,准备取名石雕效果,经改进后,可用任何颜色或者图案进行填充浮雕画面,所以取名填充浮雕效果。

下面先给出浮雕过程处理代码

procedure GetSrcColor;
asm
    push      esi           // esi = src.Scan0
    mov       eax, ecx
    sar       eax, 12
    imul      eax, ebx
    add       esi, eax      // esi += (y / 4096 * src.Stride)
    mov       eax, edx
    sar       eax, 12
    shl       eax, 2
    add       esi, eax      // esi +=  (x / 4096 * 4)
    call      _GetBilinearColor
    movd      eax, xmm0
    movd      mm0, eax
    punpcklbw mm0, mm7      // return mm0 = ARGB (word * 4)
    pop       esi
end;

// 实填充浮雕。Data 图像数据结构, Angle 角度, Size 长度, Color 填充色
procedure ImageSolidSculpture(var Data: TImageData; Angle: Single;
  Size: LongWord; Color: TARGB);
var
  x, y, Radius: Integer;
  xDelta, yDelta: Integer;
  width, height: Integer;
  dstOffset, divSize: Integer;
  src: TImageData;
begin
  if Size > 128 then Size := 128;
  divSize := DivTab[Size];
  Radius := (Size + 1) shr 1;        // 图像边框扩展半径
  Angle := PI * Angle / 180;
  xDelta := Round(Cos(Angle) * 4096);
  yDelta := Round(Sin(Angle) * 4096);
  x := (Radius shl 12) - xDelta * Size div 2;
  y := (Radius shl 12) - yDelta * Size div 2;
  width := x + (Data.Width shl 12);
  height := Data.Height;
  if Data.AlphaFlag then
    ArgbConvertPArgb(Data);
  src := _GetExpandData(Data, Radius);
  asm
    push      esi
    push      edi
    push      ebx
    mov       eax, Data
    lea       edx, src
    call      _SetCopyRegs
    mov       dstOffset, ebx
    mov       eax, Size       // mm3 = word * 4 = Size div to mul
    movq      mm3, qword ptr MMDivTab[eax*8]
    pxor      xmm7, xmm7
    pxor      mm7, mm7        // xmm7 = mm7 = 0
    movd      mm2, Color      // mm2 = word Color
    punpcklbw mm2, mm7        
    mov       ebx, src.Stride
    mov       ecx, y          // for (; y < Height; y += 4096)
@@yLoop:                      // {
		mov       edx, x          //   for (; x < Width; x += 4096)
@@xLoop:                      //   {
    push      edx
    push      ecx
    push      edi             //     x1 = x, y1 = y
    pxor      mm1, mm1
    mov       edi, Size       //     for (i = Size - 1; Size > 0; i --)
@@addLoop:                    //     {
    call      GetSrcColor     //       mm1 += GetSrcColor(x1, y1)
    paddw     mm1, mm0
    add       ecx, yDelta     //       y1 += yDelta
    add       edx, xDelta     //       x1 += xDelta
    dec       edi
    jnz       @@addLoop       //     }
    call      GetSrcColor     //     mm0 = GetSrcColor(x1, y1)
    pmulhuw   mm1, mm3        //     mm1 /= Size
    psubw     mm1, mm0        //     mm1 -= mm0
    paddw     mm1, mm2        //     mm1 += Color
    packuswb  mm1, mm7
    pop       edi
    pop       ecx
    pop       edx
    mov       al, [edi].TARGBQuad.Alpha
    movd      [edi], mm1      //     *edi = mm1
    mov       [edi].TARGBQuad.Alpha, al
    add       edi, 4          //     edi ++
    add       edx, 1000h
    cmp       edx, width
    jl        @@xLoop         //   }
    add       edi, dstOffset
    add       ecx, 1000h
    dec       height
    jnz       @@yLoop
    emms
    pop       ebx
    pop       edi
    pop       esi
  end;
  FreeImageData(src);
  if Data.AlphaFlag then
    PArgbConvertArgb(Data);
end;

// 图案填充浮雕。Data 图像数据结构, Angle 角度, Size 长度, FillData 填充图像数据结构
procedure ImageTextureSculpt(var Data: TImageData; Angle: Single;
  Size: LongWord; const FillData: TImageData);

  // 调整fillData宽度和高度为其最大2的幂,并拷贝到Result
  function AdjustFillData: TImageData;
  asm
    push    esi
    push    edi
    push    ebx
    push    eax
    push    eax
    push    eax           // NewImageData Result param
    mov     edi, fillData
    bsr     ecx, [edi].TImageData.Width
    xor     eax, eax      // mov eax, 1
    bts     eax, ecx      // shl eax, cl (width max bit)
    bsr     ecx, [edi].TImageData.Height
    xor     edx, edx      // mov edx, 1
    bts     edx, ecx      // shl edx, cl (height max bit)
    mov     ecx, pf32bit
    call    NewImageData  // Result = NewImageData(width, height, pf32bit)
    pop     eax
    mov     edx, edi
    call    _SetCopyRegs
  @@cpyLoop:
    push    ecx
    rep     movsd
    pop     ecx
    add     esi, eax
    dec     edx
    jnz     @@cpyLoop
    pop     eax
    // (Result.Width - 1) * 4: 宽度的最大余数*4,作为x*4的掩码
    mov     edx, [eax].TImageData.Width
    dec     edx
    shl     edx, 2
    mov     [eax].TImageData.Width, edx
    // Result.Height - 1: 高度的最大余数作为y的掩码
    dec     [eax].TImageData.Height
    pop     ebx
    pop     edi
    pop     esi
  end;

var
  x, y, Radius: Integer;
  xDelta, yDelta: Integer;
  width, height: Integer;
  dstOffset: Integer;
  src, back: TImageData;
begin
  if Size > 128 then Size := 128;
  Radius := (Size + 1) shr 1;        // 图像边框扩展半径
  Angle := PI * Angle / 180;
  xDelta := Round(Cos(Angle) * 4096);
  yDelta := Round(Sin(Angle) * 4096);
  x := (Radius shl 12) - xDelta * Size div 2;
  y := (Radius shl 12) - yDelta * Size div 2;
  width := x + (Data.Width shl 12);
  height := Data.Height;
  if Data.AlphaFlag then
    ArgbConvertPArgb(Data);
  src := _GetExpandData(Data, Radius);
  back := AdjustFillData;
  asm
    push      esi
    push      edi
    push      ebx
    mov       eax, Data
    lea       edx, src
    call      _SetCopyRegs
    mov       dstOffset, ebx
    mov       eax, Size       // mm3 = word * 4 = Size div to mul
    movq      mm3, qword ptr MMDivTab[eax*8]
    pxor      xmm7, xmm7      // xmm7 = mm7 = 0
    pxor      mm7, mm7        
    mov       ebx, src.Stride
    mov       ecx, y          // for (; y < Height; y += 4096)
@@yLoop:                      // {
		mov       edx, x          //   for (; x < Width; x += 4096)
@@xLoop:                      //   {
    push      edx
    push      ecx             
    push      edi
    push      edx
    push      ecx             //     x1 = x, y1 = y
    mov       edi, Size       //     for (i = Size - 1; Size > 0; i --)
    pxor      mm1, mm1        //     {
@@addLoop:
    call      GetSrcColor     //       mm1 += GetSrcColor(x1, y1)
    paddw     mm1, mm0
    add       ecx, yDelta     //       y1 += yDelta
    add       edx, xDelta     //       x1 += xDelta
    dec       edi
    jnz       @@addLoop       //     }
@@1:
    call      GetSrcColor     //     mm0 = GetSrcColor(x1, y1)
    pmulhuw   mm1, mm3        //     mm1 /= Size
    psubw     mm1, mm0        //     mm1 -= mm0
    pop       eax
    pop       edx
    shr       eax, 12         //     fy = (y / 4096) & back.Height
//    shr       edx, 12         //     fx = (x / 4096) & back.Width
    shr       edx, 10
    and       eax, back.TImageData.Height // height is y mask
    and       edx, back.TImageData.Width  // width is x*4 mask
    bsr       ecx, back.TImageData.Stride
    shl       eax, cl
//    shl       edx, 2
    add       eax, edx
    add       eax, back.TImageData.Scan0
    movd      mm0, [eax]      //     mm0 = FillData.Scan0[fy * back.Stride + fx * 4]
    punpcklbw mm0, mm7
    paddw     mm0, mm1        //     mm0 += mm1
    packuswb  mm0, mm7
    pop       edi
    pop       ecx
    pop       edx
    mov       al, [edi].TARGBQuad.Alpha
    movd      [edi], mm0      //     *edi = mm0
    mov       [edi].TARGBQuad.Alpha, al
    add       edi, 4          //     edi ++
    add       edx, 1000h
    cmp       edx, width
    jl        @@xLoop         //   }
    add       edi, dstOffset
    add       ecx, 1000h
    dec       height
    jnz       @@yLoop
    emms
    pop       ebx
    pop       edi
    pop       esi
  end;
  FreeImageData(back);
  FreeImageData(src);
  if Data.AlphaFlag then
    PArgbConvertArgb(Data);
end;

 

从处理流程看,填充浮雕和彩色浮雕、灰色浮雕是一样的,但在像素处理上是不相同的。下面是用一个用45度角,2像素深度浮雕差值计算矩阵图来说明几种浮雕效果像素处理的差异 

各矩阵中,中间的点可以看作为要处理的像素。

灰度浮雕固定地取左上角点和右下角的差值加上128背景值 (无论深度多大,都是如此,只是矩阵中间的0多少的问题),如此一来,差异小的像素趋向于背景色,差异大的像素形成“黑白分明”的阴线和阳线,这就成了灰色的浮雕效果,浮雕深度越大,“黑白分明”的效果就越明显,当深度达到一个比较大的值时,画面上会形成加强了的正、负片以及图像原色彩3层画面的共存状态;

彩色浮雕也是固定的取邻近3个点的值减去2倍右下角点的值,由于被减的点数大于减的点数,在雕刻阴影形成时保留了很大的亮度,这就相当于给各像素加了一个不固定的背景值,所以形成彩色浮雕效果;

而填充浮雕的则是取主对角线除右下角外的各点之和的平均值,减去右下角点的值,再加上填充背景色,在背景色固定的前提下(假定128),会形成类似灰度浮雕的效果,但是由于是取主对角线除右下角外的各点之和的平均值,就相当于先对像素点做了一定的表面模糊后再减去右下角点的值,所以,当雕刻深度增大时,不会形成明显的“黑白分明”效应,而是有一定的模糊过渡带,即类似阴影的半影调。

下面是以45度角,深度为10,背景色128的灰度浮雕和填充浮雕效果比较图:

 

左上角是原图,左下角是灰度浮雕效果图,右上角是填充浮雕效果图,因为填充浮雕在浮雕效果处理前对原图作了灰度处理,为便于比较,所以右下角是灰度化后的灰色浮雕效果图。

从比较图上可以看出,当浮雕深度较大时,无论是否去色,灰度浮雕效果的阳线带和阴线带都很明显,而填充浮雕则存在过渡半影调,所以,看起来有石雕的效果。另外,从灰色背景上看,填充浮雕效果明显的平坦于灰色浮雕效果,这是因为填充浮雕处理有一定的表面模糊作用的缘故。

填充浮雕可以以任意颜色和图案作为背景填充,这就使得填充浮雕过程能产生各种效果的浮雕图,为了便于使用,我把填充浮雕过程写成了2个过程,分别用来处理实色填充和图案填充。

下面先对实色填充作几个图片处理测试:

TGpBitmap测试代码(仿玉石浮雕效果)

 

procedure TForm1.Button3Click(Sender: TObject);
var
  bmp: TGpBitmap;
  g: TGpGraphics;
  data: TImageData;
begin
  bmp := TGpBitmap.Create('..\media\graysource.bmp');
  g := TGpGraphics.Create(Canvas.Handle);
  g.DrawImage(bmp, 0, 0);
  data := LockGpBitmap(bmp);
  ImageSolidSculpture(data, 45, 10, $AEC0C8);
  UnlockGpBitmap(bmp, data);
  g.DrawImage(bmp, 0, data.Height);
  g.Free;
  bmp.Free;
end;

 

效果图如下,上面是原图,下面左边是45度,深度为5的浮雕效果,下面右边是45度,深度为10的浮雕效果。

 

 

原图:

 

你是否觉得象玉石浮雕?特别是右边图中那些重叠着的叶片, 真有着一种晶莹剔透的感觉!

 

下面是以角度30,深度8,填充颜色为$A5140A的仿玛瑙色浮雕效果图,测试代码就不贴了:

 

TBitmap填充图案测试代码:

 

procedure TForm1.Button4Click(Sender: TObject);
var
  bmp, fbmp: TBitmap;
  data, fdata: TImageData;
begin
  bmp := TBitmap.Create;
  fbmp := TBitmap.Create;
  bmp.LoadFromFile('..\media\graysource.bmp');
  fbmp.LoadFromFile('..\media\back-2.bmp');
  data := GetBitmapData(bmp);
  fData := GetBitmapData(fbmp);
  ImageTextureSculpt(data, 45, 5, fData);
  Canvas.Draw(0, 0, bmp);
  fbmp.Free;
  bmp.free;
end;


下面是使用4种不同填充图案调用ImageTextureSculpture过程分别形成的浮雕效果图(原图在文章前面),角度50,浮雕深度从左上角开始,依次是8657

 

填充浮雕前也可以不进行灰度处理,有时还能产生意想不到的效果,下面是使用图案填充的2幅效果:

填充背景图案:

通过几天的研究和测试,发现浮雕角度、深度和填充色(图案)的选择,应根据图片的实际情况确定,如上图中,左上角的填充图案颜色较深、较杂,这就需要把浮雕深度用大一点,反之,如左下角就要相应小一点;图像较平淡的,不宜用很深的浮雕;浮雕角度与图像光照方向基本对应;填充颜色(图案)也应按图像具体选用,如前面的玉雕效果图,如果换成其它图片,效果不一定那么好,反过来也可以说,那张图用玛瑙色效果就差多了。

关于填充效果图的介绍就到此为止,欢迎朋友们提出改进意见。

最后重申一下,该效果确是本人无意中搞出来的,如果已经有类似的方法,纯属巧合,本人不和你争“专利权”了。

    《Delphi图像处理》系列使用GDI+单元下载地址和说明见文章《GDI+ for VCL基础 -- GDI+ 与 VCL》。

    因水平有限,错误在所难免,欢迎指正和指导。邮箱地址:maozefa@hotmail.com

    这里可访问《Delphi图像处理 -- 文章索引》。

 

2017-10-04 17:14:21 qq_30311601 阅读数 8648
  • 1.14.ARM裸机第十四部分-LCD显示器

    本期课程主要讲解LCD。首先讲了LCD显示原理、颜色原理、LCD显示器的主要性能参数、概念等,然后进入了LCD编程实战部分,用6节课从零开始带大家编写程序在LCD上显示像素、背景、线条、文字、图片等内容。本课程的学习目标是掌握LCD显示相关的概念和编程方法,对显存的作用和填充有本质的了解,对图像显示有一定的认识。

    8393 人正在学习 去看看 朱有鹏

(这里的二值为0和255)

二值图像的孔洞填充是基于图像形态学操作的基本运算,本文参考数字图像处理(冈萨雷斯著)相关章节的有关内容并结合作者自己的思考,给出了基于C# 二值图像孔洞填充的可行程序。

基础知识:参考数字图像处理 P402-P415

数学形态学的语言是集合论,这里所说的孔洞是二值图像内部八连通点阵组成的闭合圈内的像素点集,孔洞填充的基本步骤如下:
1.确定二值图像像素[0,0]为初始种子点,这里认为[0,0]像素点为背景点,而非某个孔洞内部的点。

2.以种子点为起点,采用形态学膨胀算法对背景进行填充。膨胀到不能膨胀为止。膨胀运算采用四连通结构元。

 

 


四连通结构元(即中心像素为种子,以四连通的方式向周围膨胀):

 

 

3.背景填充结束后,对得到的二值图像取反得到新的二值图像,此时图像为全部孔洞的点集。

4.将第三步骤得到的二值图像与原二值图像相加及得到孔洞填充的结果。

 

这样的算法对于大多数图像有效,然而对于[0,0]位置的像素,若为某孔洞内部的点则无法实现有效的孔洞填充。为了解决这一问题,这里采用拓展图像的方法,即在原图像的上下左右分别增加一行或一列数值为255的像素。使原图像尺寸由a*b 变为(a+2)*(b+2),以增加的四周全部像素或[0,0]处的像素为种子,对原图像进行膨胀运算。

结果如下:(图中未被填充“孔洞”是由于边缘未闭合)

         





2015-05-17 22:22:17 w12345_ww 阅读数 1838
  • 1.14.ARM裸机第十四部分-LCD显示器

    本期课程主要讲解LCD。首先讲了LCD显示原理、颜色原理、LCD显示器的主要性能参数、概念等,然后进入了LCD编程实战部分,用6节课从零开始带大家编写程序在LCD上显示像素、背景、线条、文字、图片等内容。本课程的学习目标是掌握LCD显示相关的概念和编程方法,对显存的作用和填充有本质的了解,对图像显示有一定的认识。

    8393 人正在学习 去看看 朱有鹏

【漫水填充法】

首先,漫水填充在图像处理中是做什么的?

漫水填充,经常被用来标记或分离图像中的一部分,以便对其进行进一步的处理或者分析。漫水填充也可以用来从输入图像获取掩码区域,掩码会加速处理过程,或只处理掩码指定的像素点。

漫水填充的过程是怎样的?

漫水填充操作的结果总是在某个连续的区域。当邻近像素点位于给定的范围(从loDiff到upDiff)内或在原始seedPoint像素值范围内事,cvFloodFill()函数将为这个点涂上颜色。可以选参数mask控制漫水法填充。OpenCV中给出了函数cvFloodFill(),下面简介下这个函数。

void cvFloodFill(
    IplImage*     img,//输入图像,8位或者浮点类型的单通道或三通道图
    CvPoint       seedPoint, //种子坐标
    CvScalar      newVal,//像素点被染色的值
    CvScalar      loDiff= cvScalarAll(0),//像素值的下限差值
    CvScalar      upDiff= cvScalarAll(0),//像素值的上限差值
    CvConnectedComp* comp=NULL,
    int           flags=4,//这个参数略复杂,下文介绍下

    //mask是掩码,既可以作为输入,也可以作为输出,若mask非空,
    //那么它必须为一个单通道、8位、源图像为width * height,
    //则掩码大小为 (width+2) * (height+2)
    CvArr*        mask=NULL 
)

关于flags参数,低八位可以被设置为4或者8,这个参数控制着填充算法的连通性,4表示在4个方向考虑连通性(上下左右), 8表示在8个方向考虑连通性(加上4个对角线方向),高八位可以设置CV_FLOODFILL_FIXED_RANGE,或者 CV_FLOODFILL_MASK_ONLY(如果设置只考虑填充MASK),flags的中间比特(8-15位)的值可以设置填充掩码的值 flags = 8 | CV_FLOODFILL_MASK_ONLY | CV_FLOODFILL_FIXED_RANGE | (43<<8)

下面给出漫水填充法函数应用的示例:

#include<iostream>
#include<opencv2/opencv.hpp>
using namespace std;

int main()
{
    IplImage* src = cvLoadImage("1.jpg", CV_LOAD_IMAGE_UNCHANGED);
    cvShowImage("source", src);
    IplImage* image1 = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 3);
    IplImage* image2 = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 3);

    cvCopy(src, image1);
    cvCopy(src, image2);

    cvFloodFill(
        image1,
        cvPoint(54, 82),
        CV_RGB(255, 0, 0),
        cvScalar(20, 30, 40, 0),
        cvScalar(20, 30, 40, 0),
        NULL,
        4,
        NULL
        );
    cvShowImage("dst1", image1);

    cvFloodFill(
        image2,
        cvPoint(88,88),
        CV_RGB(255, 0, 0),
        cvScalar(20, 30, 40, 0),
        cvScalar(20, 30, 40, 0),
        NULL,
        4,
        NULL
        );
    cvShowImage("dst2", image2);

    cvWaitKey();
    cvReleaseImage(&src);
    cvReleaseImage(&image1);
    cvReleaseImage(&image2);
    cvDestroyAllWindows();

    return 0;

}

【尺寸调整】

首先,尺寸调整,顾名思义,就是将某种尺寸的图像转换为其他尺寸的图像。在OpenCV中有函数cvResize(),此函数可以将源图像精确转换为目标图像的尺寸。若源图像设置了ROI,那么函数会对ROI调整尺寸,以匹配目标图像。函数比较简单。

void cvResize(
    const  CvArr*  src,
    CvArr*         dst,
    int            interpolation=CV_INTER_LINEAR //插值方法
)

关于interpolation,有如下列表:

这里写图片描述

一般情况下,我们期望源图像和重采样后的目标图像之间的映射尽可能平滑,而参数 interpolation就是控制如何映射。当缩小图像时,目标图像的像素会映射为源图像中的多个像素,这时需要插值。当放大图像时,目标图像上的像素可能无法在源图像中找到精确对应的像素,也需要进行插值。

CV_INTER_NN: 将目标图像各点的像素值设为源图像中与其距离最近点的像素值
CV_INTER_LINEAR: 将根据图像附近的4个(2×2范围)邻近像素的线性加权计算得出,权重由4个像素到精确目标点的距离决定
CV_INTER_AREA: 新的像素点覆盖原来的像素点,然后求取覆盖区域的平均值
CV_INTER_CUBIC: 首先对源图像附近的4×4个邻近像素进行三次样条拟合,然后将目标图像对应的三次样条值作为目标图像对应像素点的值

2016-11-25 16:03:15 oHanTanYanYing 阅读数 3671
  • 1.14.ARM裸机第十四部分-LCD显示器

    本期课程主要讲解LCD。首先讲了LCD显示原理、颜色原理、LCD显示器的主要性能参数、概念等,然后进入了LCD编程实战部分,用6节课从零开始带大家编写程序在LCD上显示像素、背景、线条、文字、图片等内容。本课程的学习目标是掌握LCD显示相关的概念和编程方法,对显存的作用和填充有本质的了解,对图像显示有一定的认识。

    8393 人正在学习 去看看 朱有鹏

所谓图像的轮廓填充,是建立在图像的轮廓已然查找完成的情况下的,以下面图像为例:
这里写图片描述
我们首先需要查找到图像中的圆形和正方形的几个轮廓,之后才能对这些轮廓进行处理(查找的过程我们用到OpenCV的findContours函数)。
在得到轮廓之后,难点就转变为如何填充轮廓了,对于左上角的圆来说,直接填充即可,然而对于圆环和“田”字,则一般只希望填充两个轮廓直接的区域,中间的孔洞则保留,因此在对轮廓进行填充之前需要做进一步的判断工作(通过判断findContours函数的第三个参数对象实现),具体的实现代码如下:

#include "opencv2/highgui/highgui.hpp"  
#include "opencv2/imgproc/imgproc.hpp"  
#include "time.h" 
using namespace cv;
using namespace std;

int main(int argc, char** argv)
{
    long time = clock();
    int r = 100;
    Mat src = Mat::zeros(Size(8 * r, 4 * r), CV_8UC1);
    //绘制轮廓,因为线段本身有粗细,则绘制一个圆会检测出两个轮廓,需要特别注意
    //绘制三个圆,其中一个嵌套着另一个
    circle(src, cvPoint(2 * r, 2 * r), 80, Scalar(255), 2);
    circle(src, cvPoint(2 * r, 2 * r), 50, Scalar(255), 2);
    circle(src, cvPoint(r, r), 30, Scalar(255), 2);
    //绘制一个田字
    rectangle(src, cvPoint(4 * r - r / 2, 2 * r - r / 2), cvPoint(4 * r + r / 2, 2 * r + r / 2), Scalar(255), 2);
    rectangle(src, cvPoint(4 * r - r * 2 / 5, 2 * r - r * 2 / 5), cvPoint(4 * r - r * 1 / 20, 2 * r - r * 1 / 20), Scalar(255), 2);
    rectangle(src, cvPoint(4 * r - r * 2 / 5, 2 * r + r * 1 / 20), cvPoint(4 * r - r * 1 / 20, 2 * r + r * 2 / 5), Scalar(255));
    rectangle(src, cvPoint(4 * r + r * 2 / 5, 2 * r - r * 2 / 5), cvPoint(4 * r + r * 1 / 20, 2 * r - r * 1 / 20), Scalar(255), 2);
    rectangle(src, cvPoint(4 * r + r * 2 / 5, 2 * r + r * 1 / 20), cvPoint(4 * r + r * 1 / 20, 2 * r + r * 2 / 5), Scalar(255), 2);

    Mat raw_dist1(src.size(), CV_32FC1);
    vector<vector<Point> > contours; vector<Vec4i> hierarchy;
    Mat src_copy = src.clone();
    findContours(src_copy, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_NONE);//查找轮廓,并以树状图结果存储轮廓信息
    for (size_t i = 0; i < contours.size(); i++)
    {
        if (hierarchy[i][3] != -1)//表示其为某一个轮廓的内嵌轮廓
        {
            if (hierarchy[hierarchy[i][3]][3] == -1)//表示其为最外层轮廓,上面检测到的是其线条的内部
            {
                drawContours(raw_dist1, contours, i, Scalar(255), -1);
            }
            else
            {
                drawContours(raw_dist1, contours, i, Scalar(0), -1);
            }
        }
        else
        {
            drawContours(raw_dist1, contours, i, Scalar(0), -1);
        }
    }
    printf("花费时间%dms\n", clock() - time);
    char* source_window = "Source";
    namedWindow(source_window, CV_WINDOW_AUTOSIZE);
    imshow(source_window, src);
    namedWindow("Distance1", CV_WINDOW_AUTOSIZE);
    imshow("Distance1", raw_dist1);
    imwrite("轮廓查找图像.jpg", src);
    imwrite("轮廓查找完成图像.jpg", raw_dist1);
    waitKey(0);
    return(0);
}

通过上面的代码,我们把图像中的轮廓进行了白色的填充,结果如下图所示
这里写图片描述
到此,我们得到了一副轮廓填充完成的图像,其中的填充区域为白色,如果需要进一步处理,则可以通过连通域检测算法找到各个轮廓像素点的集合。
需要注意的是,上面的代码只支持到两层轮廓的嵌套(一般而言,对于CAD等软件出来的图形最多只会有两层),再多层的嵌套则需要修改代码中的判断部分。

没有更多推荐了,返回首页