⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 stereoimg.pas

📁 这是我用Delphi和Matlab写的一个程序
💻 PAS
字号:
unit StereoImg;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, ExtCtrls, Math, Dialogs;

type
  TDistribution = (dMove, dUniform, dTriangular, dPiecewise, dNormal);

  TStereoImg = class(TComponent)
  private
    { Private declarations }
    blockW, blockH: Integer; //  图像子块的宽与高(单位:像素)
    wN, hM: Integer; //  最后一列的宽度和最后一行的高度
    FM: Integer;                        //  内部存储字段(存储字段)
    FN: Integer;
    InsPoint: TPoint;                   //  记录下个图块的插入位置
    procedure SetM(const Value: Integer);
    procedure SetN(const Value: Integer);  //  写方法,Value是常量参数(参见《Delphi4.0实用编程》P200)
    procedure ProduceKmn(Distribution: TDistribution);  //  产生一个符合要求的随机变量矩阵
    function fit(i, j: Integer; Rnd: Single): Boolean;
  protected
    { Protected declarations }
  public
    { Public declarations }
    Kmn: array of array of Single; //  随机变量矩阵Kmn是一个二维动态数组
    lImg: TBitmap;                      //  左眼图像
    rImg: TBitmap;                      //  右眼图像(原始图像)
    function Produce_rImg(Distribution: TDistribution): Boolean;  //  生成左眼视图

  published
    { Published declarations }
    property M: Integer read FM write SetM;  //  read进行直接访问、write进行方法访问(参见《Delphi4.0实用编程》P460)
    property N: Integer read FN write SetN;
  end;

procedure Register;

implementation

uses Controls;

procedure Register;
begin
  RegisterComponents('Samples', [TStereoImg]);
end;

{ TStereoImg }

{
   检查是否满足舒适立体融像区的限制条件
   这里采用《立体成像系统数学模型和视差控制方法》中的控制方法。
   2h是瞳距,一般为6.5cm;η为视锐度,取2.907e-4 rad ;
   投影平面与观察者的距离为pd(pd>0),这里取100cm,
   E是瞳孔直径,一般为0.4cm,pσ是监视器的像素间隔,这里取3.53e-2 cm;
   因此,-13 <= q_j_max <= 13,这个取值范围也满足q_j_max的理论限制。
}
function TStereoImg.fit(i, j: Integer; Rnd: Single): Boolean;
var
  k: Integer;                           //  循环变量
  sKst: Single;                         //  sKst = Ks1 + Ks2 + ... + Kst-1
  q_j: array of Double; //  用于存储一个子块中各个像素的水平视差的绝对值,
  //  像素长度应为0 ~ blockW - 1或0 ~ wN - 1,即为blockW
  q_j_max: Integer; //  用于存储数组q_j中的最大值的近似值
begin
  Result := False;
  //  在这个函数调用之前,随机变量矩阵中已经有数据了,
  //  并且blockW、blockH、wN、hM也已经在成员函数Produce_rImg中赋值,
  //  水平视差的单位是像素

  {  计算第i行第j列(i、j从0开始计算)的图像子块经变换后得到其像素点的水平坐标  }
  if j = N - 1 then //  最后一列的宽度不一定是blockW,而是wN,所以要单独讨论
  begin
    //  在内存中创建一维动态数组
    SetLength(q_j, wN);
    //  计算第i行前面j-1列的随机数之和,即计算论文中的Ks1+Ks2+...+Kst-1
    sKst := 0;
    for k := 0 to j - 1 do
      sKst := sKst + Kmn[i, k];

    //  计算各个像素的水平视差的绝对值
    for k := 0 to wN - 1 do //  像素长度应为0 ~ blockW - 1,元素个数应该是blockW
      q_j[k] := Abs(blockW * ((j - sKst) + k * (1 - Rnd) / wN));
    //  判断数组q_j中的最大值是否满足舒适立体融像区的限制条件
    q_j_max := Round(MaxValue(q_j));
    if q_j_max < 13 then
      Result := True;                   //  符合要求
  end
  else
  begin
    {  一般情况  }
    //  在内存中创建一维动态数组
    SetLength(q_j, blockW);
    //  计算第i行前面j-1列的随机数之和,即计算论文中的Ks1+Ks2+...+Kst-1
    sKst := 0;
    for k := 0 to j - 1 do
      sKst := sKst + Kmn[i, k];

    //  计算各个像素的水平视差的绝对值
    for k := 0 to blockW - 1 do //  像素长度应为0 ~ blockW - 1,元素个数应该是blockW
      q_j[k] := Abs(blockW * ((j - sKst) + k * (1 - Rnd) / blockW));

    //  判断数组q_j中的最大值是否满足舒适立体融像区的限制条件
    q_j_max := Round(MaxValue(q_j));
    if q_j_max < 13 then
      Result := True;                   //  符合要求
  end;
end;

{  产生一个符合要求的随机变量矩阵  }
procedure TStereoImg.ProduceKmn(Distribution: TDistribution);
var
  i, j: Integer;
  r: real;
begin
  //  在内存中创建二维动态数组
  SetLength(Kmn, M, N);

  //  产生随机数,M行,N列
  for i := 0 to M - 1 do
  begin
    //  行与行之间的随机数没有关系,而每一行内部各图像子块之间对应的随机数是有依赖关系的
    //  这里采取的方法是,如果某一图像子块对应的随机数不符合要求,这一行对应的随机数就全部重新产生
    j := 0;
    repeat
      //  每产生一个随机数,就验证该随机数是否符合要求

      {  选择随机变量服从哪一种分配  }
      case Distribution of
        dUniform: Kmn[i, j] := 0.5 + Random;  //  随机数范围0.5<=x<1.5,均值a应该等于1
        dTriangular:
          begin                         //  sin函数
            r := Random;
            Kmn[i, j] := ArcSin(2 * r - 1) / Pi + 1;
          end;
        dPiecewise:
          begin //  分段均匀分布β=0.82([0.5,1.32], [0.68 ,1.5])相对最优,但也最慢
            if j < (N / 2) then
              Kmn[i, j] := 0.5 + 0.82 * Random
            else
              Kmn[i, j] := 0.68 + 0.82 * Random;
          end;
        dNormal:                        //  高斯正态分布(1,1/6)
          begin
            repeat                      //  Kmn[i, j] := RandG(1,1/6);
              r := RandG(1, 1 / 6);
            until (r >= 0.5) and (r <= 1.5);
            Kmn[i, j] := r;
          end;
      end;
      
      if fit(i, j, Kmn[i, j]) then //  验证产生的随机数是否符合要求
        Inc(j)
      else
        j := 0;                         //  不符合要求就重新来过
    until j = N;
  end;
end;

{  生成左眼视图  }
//  TImage和TBitmap的性质
//  当Autosize为True时
//  TImage.Picture.Bitmap.Width = TImage.Width
//  TImage.Picture.Bitmap.Height = TImage.Height
//  TImage.Picture.Bitmap.Canvas = TImage.Canvas
//  TBitmap的像素范围: 0 - Width, 0 - Height, 不可以从1开始
//  拼接图的时候每一块不需要间隔一个像素,而是直接从上一块的边开始接
function TStereoImg.Produce_rImg(Distribution: TDistribution): Boolean;
var
  i, j: Integer;
  tmpImg: TImage;
begin
  Result := False;

  if (M > 0) and (N > 0) and (not rImg.Empty) then
  begin
    //  创建左眼视图,保证左、右眼视图大小一致
    lImg := nil;
    lImg := TBitmap.Create;
    lImg.Width := rImg.Width;
    lImg.Height := rImg.Height;
    lImg.PixelFormat := rImg.PixelFormat;

    //  将右眼视图划分成M*N个图像子块,M和N根据最相近的值进行调整。
    blockW := Round(rImg.Width / N);
    N := Trunc(rImg.Width / blockW);
    wN := rImg.Width - blockW * (N - 1);

    blockH := Round(rImg.Height / M);
    M := Trunc(rImg.Height / blockH);
    hM := rImg.Height - blockH * (M - 1);

    //  产生一个符合要求的随机变量矩阵
    ProduceKmn(Distribution);

    InsPoint := Point(0, 0);            //  复位
    //  处理每一子块
    for i := 0 to M - 1 do              //  处理每一行
      for j := 0 to N - 1 do
        if (i <> M - 1) and (j = N - 1) then  //  最后一列的宽度不一定是blockW,所以要单独讨论
        begin
          //  生成一个临时的控件用于缩放
          tmpImg := TImage.Create(Self);
          with tmpImg do
          begin
            AutoSize := False;
            Stretch := True;
            //  将tmpImg拉伸成新的宽度和高度
            Width := Round(wN * Kmn[i, j]);
            Height := blockH;
            //  将图像子块拷贝到tmpImg
            Canvas.CopyRect(Rect(0, 0, Width, Height),
              rImg.Canvas, Rect(blockW * (N - 1), blockH * i, rImg.Width, blockH
              * (i + 1)));
            //  将经过拉伸处理的图像拷回左眼视图相应位置
            lImg.Canvas.Draw(InsPoint.X, InsPoint.Y, Picture.Bitmap);

            Free;
          end;

          //  确定下一个图像的插入点
          InsPoint.X := 0;              //  换行
          InsPoint.Y := blockH * (i + 1);
        end
        else
          if (i = M - 1) and (j <> N - 1) then  //  最后一行的高度不一定是blockH,所以要单独讨论
          begin
            //  生成一个临时的控件用于缩放,每次重新生成是为了防止上一次的结果影响这一次的结果
            tmpImg := TImage.Create(Self);
            with tmpImg do
            begin
              AutoSize := False;
              Stretch := True;
              //  将tmpImg拉伸成新的宽度和高度
              Width := Round(blockW * Kmn[i, j]);
              Height := hM;
              //  将图像子块拷贝到tmpImg
              Canvas.CopyRect(Rect(0, 0, Width, Height),
                rImg.Canvas, Rect(blockW * j, blockH * i, blockW * (j + 1),
                rImg.Height));
              //  将经过拉伸处理的图像拷回左眼视图相应位置
              lImg.Canvas.Draw(InsPoint.X, InsPoint.Y, Picture.Bitmap);

              Free;
            end;

            //  确定下一个图像的插入点
            InsPoint.X := InsPoint.X + tmpImg.Width;
          end
          else
            if (i = M - 1) and (j = N - 1) then  //  最后一个子块要单独讨论
            begin
              //  生成一个临时的控件用于缩放
              tmpImg := TImage.Create(Self);
              with tmpImg do
              begin
                AutoSize := False;
                Stretch := True;
                //  将tmpImg拉伸成新的宽度和高度
                Width := Round(wN * Kmn[i, j]);
                Height := hM;
                //  将图像子块拷贝到tmpImg
                Canvas.CopyRect(Rect(0, 0, Width, Height),
                  rImg.Canvas, Rect(blockW * j, blockH * i, rImg.Width,
                  rImg.Height));
                //  将经过拉伸处理的图像拷回左眼视图相应位置
                lImg.Canvas.Draw(InsPoint.X, InsPoint.Y, Picture.Bitmap);

                Free;
              end;
            end
            else
            begin
              {  一般情况  }
              //  生成一个临时的控件用于缩放
              tmpImg := TImage.Create(Self);
              with tmpImg do
              begin
                AutoSize := False;
                Stretch := True;
                //  将tmpImg拉伸成新的宽度和高度
                Width := Round(blockW * Kmn[i, j]);
                Height := blockH;
                //  将图像子块拷贝到tmpImg
                Canvas.CopyRect(Rect(0, 0, Width, Height),
                  rImg.Canvas, Rect(blockW * j, blockH * i, blockW * (j + 1),
                  blockH * (i + 1)));
                //  将经过拉伸处理的图像拷回左眼视图相应位置
                lImg.Canvas.Draw(InsPoint.X, InsPoint.Y, Picture.Bitmap);
                //  确定下一个图像的插入点
                InsPoint.X := InsPoint.X + Width;

                Free;
              end;
            end;

    Result := True;
  end;

end;

procedure TStereoImg.SetM(const Value: Integer);
begin
  FM := Value;
end;

procedure TStereoImg.SetN(const Value: Integer);
begin
  FN := Value;
end;

end.

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -