关于图像合成所引申的几个函数

偶然在论坛上看到提问,将图片B合成到图片A上,并且在A上写字

于是,随手写的一个函数,具体代码如下:

{作者:不得闲

 2009-02-11}

function HeCheng(A,b:TBitmap;const TransPercent: integer=50):TBitmap;
var
  i,j: integer;
  p1,p2: PByteArray;
  count,MinBegin: Integer;
  MinHeight: integer;
  MinWidth,MaxWidth: Integer;
  r: TRect;
begin
  A.PixelFormat := pf32bit;
  b.PixelFormat := pf32bit;

MinHeight := Min(A.Height,B.Height);
  MinWidth := Min(A.Width,B.Width);
  MaxWidth := Max(A.Width,B.Width);

MinBegin := 4 * ((MaxWidth - MinWidth) Div 2);
  count := 4 * (MaxWidth-(MaxWidth - MinWidth) Div 2 - 1);

for i := 0 to MinHeight - 1 do
  begin
    if MinHeight = B.Height then
    begin
      p1 := A.ScanLine[i];
      p2 := B.ScanLine[i];
    end
    else
    begin
      p1 := B.ScanLine[i];
      p2 := A.ScanLine[i];
    end;
    j := MinBegin;
    while j < count do
    begin
      if (p2[j - MinBegin] = 255) and (p2[j-MinBegin+1] = 255) and (p2[j-MinBegin+2]=255) then
        inc(j,4)
      else
      begin
        p1[j] := p1[j] + TransPercent * (p2[j-MinBegin] - p1[j]) div 100;
        inc(j);
      end;
    end;
  end;
  if MinHeight = B.Height then
  begin
    r.Top := A.Height - A.Canvas.TextHeight(‘你好‘)-5;
    r.Bottom := A.Height;
    r.Left := 0;
    r.Right := A.Width;
    A.Canvas.Brush.Style := bsclear;
    windows.DrawText(A.Canvas.Handle,‘你好‘,-1,r,DT_Center or DT_VCenter or DT_SIngleLine);
    Result := A;
  end
  else
  begin
    r.Top := B.Height - B.Canvas.TextHeight(‘你好‘)-5;
    r.Bottom := B.Height;
    r.Left := 0;
    r.Right := B.Width;
    B.Canvas.Brush.Style := bscleae r;
    windows.DrawText(B.Canvas.Handle,‘你好‘,-1,r,DT_Center or DT_VCenter or DT_SIngleLine);
    Result := B;
  end;
end;

这里,我先将位图A和B都转化成了pf32bit,此时每个位图的每个像素点由4个字节存储

存储的方式为 BGRL,所以,总的字节数应该是 4*宽度

过滤掉白色,也就是RGB分别为255的时候,不处理合成则可。

由上面我们又可以引申一个函数,可以用来过滤任何颜色的,也就是说,指定一个颜色,只要图片中含有该颜色的区域就过滤掉,比如,图片B中含有红蓝两色,此时设置一个红色过滤色,哪么合成之后B图中的红色被过滤掉

代码如下:

{-------------------------------------------------------------------------------
  过程名:    TColorToRGB
  作者:      不得闲
  日期:      2009.02.11
  参数:      const Color: TColor; var R, G, B: Byte
  返回值:    无
  用途:     获得颜色的RGB值
-------------------------------------------------------------------------------}

procedure TColorToRGB(const Color: TColor; var R, G, B: Byte);
var
  C: Integer;
begin
  C := ColorToRGB(Color);
  R := C and $FF;
  G := (C shr 8) and $FF;
  B := (C shr 16) and $FF;
end;
{-------------------------------------------------------------------------------
  过程名:    HeCheng
  作者:      不得闲
  日期:      2009.02.11
  参数:      A,b: 指定合成位图
            TransPercent: 设置透明度
            ignoreColor:  设置合成时忽略的颜色
            ColorOffset:  透明色的边缘色差(在该色差内的颜色都将忽略掉)

  返回值:    TBitmap
  用途:     合成两张位图
-------------------------------------------------------------------------------}

function HeCheng(A,b:TBitmap;const TransPercent: integer=50;const ignoreColor: TColor = clwhite;Const ColorOffset: byte=0):TBitmap;
var
  i,j: integer;
  p1,p2: PByteArray;
  count,MinBegin: Integer;
  MinHeight: integer;
  MinWidth,MaxWidth: Integer;
  r: TRect;
  RColor,GColor,BColor: Byte;
begin
  A.PixelFormat := pf32bit;
  b.PixelFormat := pf32bit;
  TColorToRGB(ignoreColor,RColor,GColor,BColor);
  
  MinHeight := Min(A.Height,B.Height);
  MinWidth := Min(A.Width,B.Width);
  MaxWidth := Max(A.Width,B.Width);

MinBegin := 4 * ((MaxWidth - MinWidth) Div 2);
  count := 4 * (MaxWidth-(MaxWidth - MinWidth) Div 2 - 1);

for i := 0 to MinHeight - 1 do
  begin
    if MinHeight = B.Height then
    begin
      p1 := A.ScanLine[i];
      p2 := B.ScanLine[i];
    end
    else
    begin
      p1 := B.ScanLine[i];
      p2 := A.ScanLine[i];
    end;
    j := MinBegin;
    while j < count do
    begin

//比较字节的值,位图该点像素的RGB值是否为需要过滤的颜色值,如果是,则过滤掉
      if (abs(p2[j - MinBegin] - BColor)<=ColorOffset) and

(abs(p2[j-MinBegin+1] - GColor)<=ColorOffset) and

(abs(p2[j-MinBegin+2]-RColor)<=ColorOffset)  then
        inc(j,4)
      else
      begin
        p1[j] := p1[j] + TransPercent * (p2[j-MinBegin] - p1[j]) div 100;
        inc(j);
      end;
    end;
  end;
  if MinHeight = B.Height then
  begin
    r.Top := A.Height - A.Canvas.TextHeight(‘你好‘)-5;
    r.Bottom := A.Height;
    r.Left := 0;
    r.Right := A.Width;
    A.Canvas.Brush.Style := bsclear;
    windows.DrawText(A.Canvas.Handle,‘你好‘,-1,r,DT_Center or DT_VCenter or DT_SIngleLine);
    Result := A;
  end
  else
  begin
    r.Top := B.Height - B.Canvas.TextHeight(‘你好‘)-5;
    r.Bottom := B.Height;
    r.Left := 0;
    r.Right := B.Width;
    B.Canvas.Brush.Style := bsclear;
    windows.DrawText(B.Canvas.Handle,‘你好‘,-1,r,DT_Center or DT_VCenter or DT_SIngleLine);
    Result := B;
  end;
end;

比如,现在要透明一个图片合成上去

HeCheng(Image1.Picture.Bitmap,Image4.Picture.Bitmap,100,Image4.Canvas.Pixels[0,0],20);

在加一个透明图画法的函数,效果不大好

{-------------------------------------------------------------------------------
  过程名:    TransparentDraw
  作者:      不得闲
  日期:      2009.02.12
  参数:      DestCanvas: 目标画布
             DestRect: 目标区域
             Graphic: 位图
             ColorOffset 背景色附近的颜色差值,在该差值之内的颜色,都会被透明掉
  返回值:    无
-------------------------------------------------------------------------------}

procedure TransparentDraw(DestCanvas: TCanvas;DestRect: TRect;Graphic: TBitmap;const ColorOffset: Byte=0);
var
  i,j,Count: integer;
  RectH,RectW: integer;
  p: PByteArray;
  RColor,GColor,BColor: Byte;
begin
  //区域高度
  Graphic.PixelFormat := pf32bit;
  RectH := DestRect.Bottom - DestRect.Top;
  if RectH > Graphic.Height then
    RectH := Graphic.Height;
  RectH := DestRect.Top + RectH;
  RectW := DestRect.Right - DestRect.Left;
  TColorToRGB(Graphic.Canvas.Pixels[0,0],RColor,GColor,BColor);
  if RectW > Graphic.Width then
    RectW := Graphic.Width;
  Count := 4*RectW - 1;
  for i := DestRect.Top to RectH - 1 do
  begin
    p := Graphic.ScanLine[i - DestRect.Top];
    j := 0;
    while j < Count do
    begin
      if (abs(p[j] - BColor)<=ColorOffset) and (abs(p[j+1] - GColor) <= ColorOffset)and (abs(p[j+2] - RColor)<=ColorOffset) then
        inc(j,4)
      else
      begin
        BColor := p[j];
        GColor := p[j + 1];
        RColor := p[j+2];
        DestCanvas.Pixels[j div 4,i] := RGB(RColor,GColor,BColor);
        inc(j,4);
      end;
    end;
  end;
end;

同时,也写下了一个更换图片背景色的函数,代码如下:

{-------------------------------------------------------------------------------
  过程名:    ChangeBmpBackGround
  作者:      不得闲
  日期:      2009.02.12
  参数:      bmp: TBitmap;
             ChangeToBack: 要修改为的背景色
             ColorOffset 背景色附近的颜色差值,在该差值之内的颜色,也会被修改
  返回值:    无
-------------------------------------------------------------------------------}

procedure ChangeBmpBackGround(bmp: TBitmap;ChangeToBack: TColor;const ColorOffset: Byte = 0);
var
  i,j,Count: integer;
  RColor,GColor,BColor: Byte;
  TRColor,TGColor,TBColor: Byte;
  p: PByteArray;
begin
  bmp.PixelFormat := pf32bit;
  TColorToRGB(bmp.Canvas.Pixels[0,0],RColor,GColor,BColor);
  TColorToRGB(ChangeToBack,TRColor,TGColor,TBColor);
  count := 4 * bmp.Width - 1;
  for i := 0 to bmp.Height - 1 do
  begin
    j := 0;
    p := bmp.ScanLine[i];
    while j < count do
    begin
      if (abs(p[j] - BColor)<=ColorOffset) and (abs(p[j+1] - GColor) <= ColorOffset)and (abs(p[j+2] - RColor)<=ColorOffset) then
      begin
         p[j] := TBColor;
         P[j+1] := TGColor;
         p[j+2] := TRColor;
      end;
      inc(j,4);
    end;
  end;
end;

今天在公司由于要用到一个图片遮罩的效果,于是按照同样的思路写了一个图像遮罩函数:

代码如下:

{-------------------------------------------------------------------------------
  过程名:    SoftBmp
  作者:      不得闲
  日期:      2009.02.13
  参数:      bmp: TBitmap;
            DarkRect: 不遮罩的区域
            SoftColor:指定遮罩色

            SoftPercent指定遮罩度(取1-100,100为完全遮罩)

  返回值:    无
-------------------------------------------------------------------------------}

procedure SoftBmp(bmp: TBitmap;var DarkRect: TRect;const SoftColor: TColor;const SoftPercent: Integer=50);
var
  i,j : integer;
  pB : PByteArray;
  BmpFormatXs: Integer;
  w,h:Integer;
  R,G,B: Integer;
begin
  if bmp.PixelFormat <> pf32bit then  
    bmp.PixelFormat := pf32bit;
  BmpFormatXs := 4;

w:= DarkRect.Right - DarkRect.Left;
  h:= DarkRect.Bottom - DarkRect.Top;
  
  if DarkRect.Right > bmp.Width then
  begin
    DarkRect.Left:=bmp.Width - w;
    DarkRect.Right:=bmp.Width;
  end;
  if (DarkRect.Bottom > bmp.Height) then
  begin
    DarkRect.Top:= bmp.Height - h;
    DarkRect.Bottom:=bmp.Height;
  end;
  if DarkRect.Left <0 then
  begin
    DarkRect.Left:=0;
    DarkRect.Right:=w;
  end;
  if DarkRect.Top <0 then
  begin
    DarkRect.Top:=0;
    DarkRect.Bottom:=h;
  end;
  TColorToRGB(SoftColor,R,G,B);
  for i := 0 to DarkRect.Top - 1 do
  begin
    pb:=bmp.ScanLine[i];
    j := 0;
    while j < BmpFormatXs*bmp.Width - 1 do
    begin
      pb[j] := B + (100-SoftPercent) * (pb[j] - B) div 100;
      pb[j+1] := G + (100-SoftPercent) * (pb[j+1] - G) div 100;
      pb[j+2] := R + (100-SoftPercent) * (pb[j+2]-R) div 100;
      inc(j,BmpFormatXs);
    end;
  end;

for i := DarkRect.Top to bmp.Height - 1 do
  begin
    pb:=bmp.ScanLine[i];
    j := 0;
    while j < BmpFormatXs*DarkRect.Left - 1 do
    begin
      pb[j] := B + (100-SoftPercent) * (pb[j] - B) div 100;
      pb[j+1] := G + (100-SoftPercent) * (pb[j+1] - G) div 100;
      pb[j+2] := R + (100-SoftPercent) * (pb[j+2]-R) div 100;
      inc(j,BmpFormatXs);
    end;
  end;
  for i := DarkRect.Bottom to bmp.Height - 1 do
  begin
    pb:=bmp.ScanLine[i];
    j := BmpFormatXs*DarkRect.Left;
    while j < BmpFormatXs*bmp.Width - 1 do
    begin
      pb[j] := B + (100-SoftPercent) * (pb[j] - B) div 100;
      pb[j+1] := G + (100-SoftPercent) * (pb[j+1] - G) div 100;
      pb[j+2] := R + (100-SoftPercent) * (pb[j+2]-R) div 100;
      inc(j,BmpFormatXs);
    end;
  end;

for i := DarkRect.Top to DarkRect.Bottom - 1 do
  begin
    pb:=bmp.ScanLine[i];
    j := BmpFormatXs*DarkRect.Right;
    while j < BmpFormatXs*bmp.Width - 1 do
    begin
      pb[j] := B + (100-SoftPercent) * (pb[j] - B) div 100;
      pb[j+1] := G + (100-SoftPercent) * (pb[j+1] - G) div 100;
      pb[j+2] := R + (100-SoftPercent) * (pb[j+2]-R) div 100;
      inc(j,BmpFormatXs);
    end;
  end;
end;

http://blog.csdn.net/suiyunonghen/article/details/3876813

时间: 2024-10-10 15:29:14

关于图像合成所引申的几个函数的相关文章

JS函数——作用域

一 : 作用域的相关概念 首先看下 变量作用域 的概念:一个变量的作用域是程序源代码中定义这个变量的区域.————————<javascript权威指南>第六版全局变量拥有全局作用域,函数体内定义的局部变量拥有函数作用域. 就个人理解,作用域(scope),顾名思义,是一块区域 或 领域 ,并且有某些对象(包括变量,属性,方法等)能够在这里起作用:作用域是在定义的时候决定的,和什么时候执行无关:这时候问题来了: 问题1:区域在那儿? 他是一个概念,是看不到摸不着的:在这个区域内原本是什么都没有

c/c++: c++函数返回类型什么情况带const

c++ 函数的返回类型,包括const 什么时候起作用呢? 函数返回值不想其立即修改的. 例子如下,这是一个简单的避免产生隐形返回变量的方法,abc 的函数返回是引用,main函数中第10行,++ 操作是基于 const int & 类型,所以会出错,但以后对改引用的操作不会受到const 约束. 这样的好处是避免了函数返回值与操作符的逻辑错误结合,例如下面的例子中函数返回的++,对于main 函数是不直观的,进一步的应用是在操作符重载方面,见下一情况说明. 1 const int & a

理解java reference

Java世界泰山北斗级大作<Thinking In Java>切入Java就提出“Everything is Object”.在Java这个充满Object的世界中,reference是一切谜题的根源,所有的故事都是从这里开始的. Reference是什么? 如果你和我一样在进入Java世界之前曾经浪迹于C/C++世界,就一定不会对指针陌生.谈到指针,往日种种不堪回首的经历一下子涌上心头,这里不是抱怨的地方,让我们暂时忘记指针的痛苦,回忆一下最初接触指针的甜蜜吧!还记得你看过的教科书中,如何讲

Visual Studio调试之断点基础篇

原文链接地址:http://www.cnblogs.com/killmyday/archive/2009/09/26/1574311.html 我曾经问过很多人,你一般是怎么调试你的程序的? F9, F5, F11, F-- 有很多书和文章都是介绍怎么使用Visual Studio编写WinForm啦,.ASP.NET之类的程序:知道如何编写固然重要,但是我觉得程序员可能只会花费30%的时间在编写代码上,剩下的大部分时间都是在调试程序.在网上看到很多人介绍Windbg的用法,但是没有看到几篇讲解

Redis_字典

阅读本文之前要了解的两件事情,第一,Redis是一种Key-Value数据库,第二,字典是一种保存键值对的抽象数据结构.所以不难猜出字典在Redis中应用一定非常广泛,实际上,Redis数据库的底层实现就是字典,对数据库的增删查改也是构建在对字典的操作上,那么想要深入理解Redis,字典的解密是必不可少的,接下来,就让我们一层一层解开指点的面纱,看看它的真面目. 首先看看Redis中有哪些地方使用到了字典 一, 数据库键空间 Redis是一个键值对数据库服务器,服务器中的每个数据库都是一个Red

javascript基础-arguments对象

arguments是函数内部自带的对象,它是一个类数组的存在,所谓的类数组就是没有数组的方法, 但可以通过下标来访问内部的元素,也有length属性.它的作用呢? 保存了函数调用的时候传入的实际的参数,通过length属性可知道传入参数的数量. 比如例子中的函数fn function fn(arg1,arg2,arg3,arg4) { //此时arguments.length不是4,因为它不是由声明函数时的形参数量决定的 //而是由调用fn时传入的参数的数量决定的 } fn(1,2,3); ar

[daily][optimize] 去吃面 (python类型转换函数引申的性能优化)(未完待续)

前天,20161012,到望京面试.第四个职位,终于进了二面.好么,结果人力安排完了面试时间竟然没有通知我,也没有收到短信邀请.如果没有短信邀请门口的保安大哥是不让我进去大厦的.然后,我在11号接到了面试官直接打来的电话,问我为啥还没到,我说没人通知我我不知道呀.结果我就直接被他邀请去以访客的身份参加面试了.不知道人力的姑娘是不是认识我,且和我有仇,终于可以报复了... 然后,我终于如约到了,面试官带着我去前台登记.前台的妹子更萌...认为我是面试官,面试官是才是来面试的.我气质真的那么合吗?

JAVA-初步认识-第四章-函数-两个明确-练习

一. 加强对函数使用的认识 观察下面截图中的三个需求,可以看出,之前对于功能化的理解还是过于肤浅.方法或函数的核心在于提高复用性,事物重复出现时,我们就将其独立地封装起来,需要时,直接调用.至于这个封装的东西是什么,根本不在乎. 二. 对函数在实际中的应用进行解析 注解:对于本题,两个明确都没搞懂.明确1是确定功能的结果,就是结果的类型,是数值类型还是没类型void.功能是画矩形,那就不是数值,是void.明确二说的是在实现功能的过程中,加入的参数问题. 注解:这里的else居然可以省略,还有返

用 void* 编写泛型函数

1.泛型交换 //1.编写int类型的swap void swap(int *vp1, int *vp2) { int a = *vp1; *vp1 = *vp2; *vp2 = *vp1; } //2.引申至泛型swap void swap(void *vp1, void *vp2, int size){ char buffer[size]; memcpy(buffer, vp1, size); memcpy(vp1, vp2, size); memcpy(vp2, buffer, size)