TEASOFT软件中图片之间结构保持渐变过程
简 介: 本文讨论了一个基于结构保持的图片渐变过程的实现。
关键词
: TEASOFT,渐变
§01 结构保持渐变
一、背景介绍
1、什么是图像结构保持渐变?
将两个图像进行渐变是视频中常见到的场景过渡过程。 如果在渐变过程引入结构化信息可以使得渐变之间形成特定的欣喜牵连,不仅可以达到特殊的视觉效果,同时也可以传递额外场景切换时发生的信息。
下面给出的是两张人脸之间的渐变过程。
- 左边是两个图像之间的的亮度信息进行插值后发生渐变;
- 右边是两个图像之间对于一些结构特征点(眼角,嘴角,脸庞轮廓等)保持过渡,形成结构保持渐变过程。
▲ 图1.1.1 两个人脸之间的图像渐变
通过对比可以看到具有结构保持的图像渐变显得更加的自然, 反映了两个图像之间内容结构方面的联系。
2、结构保持渐变在教学视频中的应用
在视频教学中,通常利用画笔对于视角内容进行批注。 为了保持版本中的清晰,需要对批注笔迹进行清理。 在前后两张图片之间,会存在:
- 被清理掉的笔迹;
- 重新绘制的笔迹;
如果能够在两张图片之间采用结构保持渐变,可以使得批注焦点的改变能够清晰的显示出来,抓住观看人的注意力。
▲ 图1.2.2 两个具有不同笔迹批注的画面
3、算法测试平台
下面是在 TEASOFT教学软件 中在 SetBackgroundPicture中编程实现上述图像结构保持渐变过程。
- 编程测试环境:
-
编程平台
:C++Builder
文件
:BoardRec.cpp
函数
:ShowPictureOnImage
程序行
:47087
二、实现过程
1、获得消失与新增笔画
下面算法中,假设图像的背景,也就是没有任何显示内容时,对应的颜色为白色(clWhite)。
▲ 图1.2.1 参与测试的前后两张图片
下面通过三步分别获得消失与新增的笔画。
- 步骤1: 前后两个图像进行XOR:
通过前后两个图像进行异或,获得消失与新增的笔画。但这两个笔画是被背景(白色背景)异或-也就是取反的。
▲ 图1.2.2 前后两个图像取异或(XOR)
- 步骤2: 对结果取反:
对结果取反,形成前后两个图像。
▲ 图1.2.3 消失与新增的笔画
- 步骤3: 分别与前后两张图像进行OR,获得消失与新增笔画:
▲ 图1.2.4 分别与前面图像和后面图像进行OR, 或者消失与新增画笔
2、获得笔画图片块
将图像划分为若干图像块, 设置图片块的边长。
- 图片块编程: l s i d e = 10 l_{side} = 10 lside=10 。这个参数是可变的;
- 笔画阈值: r = 0.3 r = 0.3 r=0.3 ;这个参数用于判断图像块是否是笔画还是背景;
当图片块内的像素不是背景颜色比例超过 r r r 之后,判断改图像块是笔画,否则就是背景。
▲ 图1.2.5 笔画图片块
3、移动图片块
为了实现渐变, 将前面笔画方块移动到后面笔画所在的位置。
▲ 图1.2.6 从前笔画的方块移动到后笔画对应的位置
这个过程需要确定两方面的问题。
(1)方块对应关系
前后笔画所处的位置,个数各不相同,那么它们之间的对应关系就会有很多种方式。
Ⅱ.顺序关系
简单采用生成扫描顺序完成对应关系。
Ⅲ.对应关系
只有当前后两个笔画图片块数量想通的时候,对应关系就是按照顺序一一对应;
如果前后两个笔画图片块数量不同,对前面图片块进行抽样, 数量与后面图片笔画保持一致;
(2)移动渐变过程
在整个移动过程前一半时间,所有的图像块来自于前面笔画; 当移动过程进入到后半程,所有的图像块来自于后面出现的笔画。
实际上,如果能够在这个过程中保持它们之间的灰度渐变可能效果更好。
在移动前,需要先清理出一个不带有笔画的图像。也就是在前一个图像数据的基础上,将笔画抹去,即吧所有的图像块填充为背景颜色(白色)。
▲ 图1.2.7 图片块移动
※ 总 结 ※
本文讨论了一个基于结构保持的图片渐变过程的实现。
一、实现代码
} else if(nDrawMode == SBGP_SHOWMODE_MOVE) {
int image_width = pImage->Picture->Width;
int image_height = pImage->Picture->Height;
if(image_width == nOnWidth ||
image_height == nOnHeight) {
TImage * pImage1 = new TImage(MainForm);
pImage1->Width = nOnWidth;
pImage1->Height = nOnHeight;
pImage1->Picture->Bitmap->Width = nOnWidth;
pImage1->Picture->Bitmap->Height = nOnHeight;
TImage * pImage2 = new TImage(MainForm);
pImage2->Width = image_width;
pImage2->Height = image_height;
pImage2->Picture->Bitmap->Width = image_width;
pImage2->Picture->Bitmap->Height = image_height;
TImage * pImage4 = new TImage(MainForm);
pImage4->Width = image_width;
pImage4->Height = image_height;
pImage4->Picture->Bitmap->Width = image_width;
pImage4->Picture->Bitmap->Height = image_height;
TImage * pImage3 = new TImage(MainForm);
pImage3->Width = 100;
pImage3->Height = 100;
pImage3->Picture->Bitmap->Width = 100;
pImage3->Picture->Bitmap->Height = 100;
pImage3->Canvas->Brush->Style = bsSolid;
pImage3->Canvas->Brush->Color = clWhite;
BitBlt(pImage1->Canvas->Handle, 0, 0, nOnWidth, nOnHeight,
pOnImage->Canvas->Handle, 0, 0, SRCCOPY);
BitBlt(pImage1->Canvas->Handle, 0, 0, nOnWidth, nOnHeight,
pImage->Canvas->Handle, 0, 0, SRCINVERT);
StretchBlt(pImage1->Canvas->Handle, 0, 0, nOnWidth, nOnHeight,
pImage3->Canvas->Handle, 0, 0, 100, 100, SRCINVERT);
BitBlt(pImage2->Canvas->Handle, 0, 0, nOnWidth, nOnHeight,
pImage1->Canvas->Handle, 0, 0, SRCCOPY);
BitBlt(pImage1->Canvas->Handle, 0, 0, nOnWidth, nOnHeight,
pOnImage->Canvas->Handle, 0, 0, SRCPAINT);
BitBlt(pImage2->Canvas->Handle, 0, 0, nOnWidth, nOnHeight,
pImage->Canvas->Handle, 0, 0, SRCPAINT);
int nBlockSide = 10;
float fThreshold = 0.15;
#define MAX_BLOCK 1024
#define BLOCK_MOVE_STEP 30
int nBlock1Dim[MAX_BLOCK][2];
int nBlock2Dim[MAX_BLOCK][2];
int nBlock1Number = 0;
int nBlock2Number = 0;
BitBlt(pImage4->Canvas->Handle, 0, 0, nOnWidth, nOnHeight, pImage->Canvas->Handle, 0, 0, SRCCOPY);
BitBlt(pImage->Canvas->Handle, 0, 0, nOnWidth, nOnHeight, pImage1->Canvas->Handle, 0, 0, SRCCOPY);
nBlock1Number = Image2BlockPosition(pImage, nBlockSide, fThreshold, (int *)nBlock1Dim, MAX_BLOCK);
BitBlt(pImage->Canvas->Handle, 0, 0, nOnWidth, nOnHeight, pImage2->Canvas->Handle, 0, 0, SRCCOPY);
nBlock2Number = Image2BlockPosition(pImage, nBlockSide, fThreshold, (int *)nBlock2Dim, MAX_BLOCK);
// Image2BlockShow(pOnImage, (int *)nBlock1Dim, nBlock1Number, nBlockSide, clGreen);
// Image2BlockShow(pOnImage, (int *)nBlock2Dim, nBlock2Number, nBlockSide, clBlue);
BitBlt(pImage->Canvas->Handle, 0, 0, nOnWidth, nOnHeight, pImage4->Canvas->Handle, 0, 0, SRCCOPY);
BitBlt(pImage2->Canvas->Handle, 0, 0, nOnWidth, nOnHeight, pOnImage->Canvas->Handle, 0, 0, SRCCOPY);
int nReturnFlag = 0;
if(nBlock1Number > 10 && nBlock2Number > 10) {
nReturnFlag = 1;
int i;
pOnImage->Canvas->Brush->Style = bsSolid;
pOnImage->Canvas->Brush->Color = g_MemoBoard.m_Color;
pOnImage->Canvas->Pen->Style = psSolid;
pOnImage->Canvas->Pen->Color = g_MemoBoard.m_Color;
for(i = 0; i < nBlock1Number; i ++) {
int nLeft = nBlock1Dim[i][0];
int nTop = nBlock1Dim[i][1];
int nRight = nLeft + nBlockSide;
int nBottom = nTop + nBlockSide;
pOnImage->Canvas->FillRect(TRect(nLeft, nTop, nRight, nBottom));
}
BitBlt(pImage4->Canvas->Handle, 0, 0, nOnWidth, nOnHeight, pOnImage->Canvas->Handle, 0, 0, SRCCOPY);
for(i = 1; i < BLOCK_MOVE_STEP; i ++) {
BitBlt(pOnImage->Canvas->Handle, 0, 0, nOnWidth, nOnHeight, pImage4->Canvas->Handle, 0, 0, SRCCOPY);
for(int j = 0; j < nBlock2Number; j ++) {
int nID1 = j * nBlock1Number / (nBlock2Number - 1);
int nX1 = nBlock1Dim[nID1][0];
int nY1 = nBlock1Dim[nID1][1];
int nX2 = nBlock2Dim[j][0];
int nY2 = nBlock2Dim[j][1];
int nX = (nX1 * (BLOCK_MOVE_STEP - i) + nX2 * i) / BLOCK_MOVE_STEP;
int nY = (nY1 * (BLOCK_MOVE_STEP - i) + nY2 * i) / BLOCK_MOVE_STEP;
BitBlt(pOnImage->Canvas->Handle, nX, nY, nBlockSide, nBlockSide,
pImage1->Canvas->Handle, nBlock1Dim[nID1][0], nBlock1Dim[nID1][1], SRCAND);
}
pOnImage->Refresh();
}
}
BitBlt(pOnImage->Canvas->Handle, 0, 0, nOnWidth, nOnHeight, pImage->Canvas->Handle, 0, 0, SRCCOPY);
delete pImage1, pImage2, pImage3, pImage4;
if(nReturnFlag == 1) {
pOnImage->Refresh();
return 0;
}
BitBlt(pOnImage->Canvas->Handle, 0, 0, nOnWidth, nOnHeight, pImage2->Canvas->Handle, 0, 0, SRCCOPY);
// pOnImage->Refresh();
}
int nRect[4], nImageBox[4];
nRect[0] = nShowLeft;
nRect[1] = nShowTop;
nRect[2] = nOnWidth + nShowLeft;
nRect[3] = nOnHeight + nShowTop;
GetFullFitImageBoxMax(nImageWidth, nImageHeight, nRect, nImageBox);
rect = TRect(nImageBox[0], nImageBox[1], nImageBox[2], nImageBox[3]);
TImage * pImage1 = new TImage(MainForm);
pImage1->Width = nOnWidth;
pImage1->Height = nOnHeight;
pImage1->Picture->Bitmap->Width = nOnWidth;
pImage1->Picture->Bitmap->Height = nOnHeight;
TImage * pImage2 = new TImage(MainForm);
pImage2->Width = image_width;
pImage2->Height = image_height;
pImage2->Picture->Bitmap->Width = image_width;
pImage2->Picture->Bitmap->Height = image_height;
TImage * pImage3 = new TImage(MainForm);
pImage3->Width = 100;
pImage3->Height = 100;
pImage3->Picture->Bitmap->Width = 100;
pImage3->Picture->Bitmap->Height = 100;
pImage3->Canvas->Brush->Style = bsSolid;
pImage3->Canvas->Brush->Color = clWhite;
BitBlt(pImage1->Canvas->Handle, 0, 0, nOnWidth, nOnHeight,
pOnImage->Canvas->Handle, 0, 0, SRCCOPY);
int MERGE_STEP = 24;
for(int i = 0; i < MERGE_STEP; i ++) {
int nColor = 0xff * (i + 1) / (MERGE_STEP + 1);
TColor color = TColor((nColor << 16) + (nColor << 8) + nColor);
pImage3->Canvas->Brush->Color = color;
pImage3->Canvas->FillRect(TRect(0, 0, 100, 100));
BitBlt(pImage2->Canvas->Handle, 0, 0, image_width, image_height,
pImage->Canvas->Handle, 0, 0, SRCCOPY);
StretchBlt(pImage2->Canvas->Handle, 0, 0, image_width, image_height,
pImage3->Canvas->Handle, 0, 0, 100, 100, SRCAND);
nColor = 0xff - 0xff * (i + 1) / (MERGE_STEP + 1);
color = TColor((nColor << 16) + (nColor << 8) + nColor);
pImage3->Canvas->Brush->Color = color;
pImage3->Canvas->FillRect(TRect(0, 0, 100, 100));
BitBlt(pOnImage->Canvas->Handle, 0, 0, nOnWidth, nOnHeight,
pImage1->Canvas->Handle, 0, 0, SRCCOPY);
StretchBlt(pOnImage->Canvas->Handle, 0, 0, nOnWidth, nOnHeight,
pImage3->Canvas->Handle, 0, 0, 100, 100, SRCAND);
StretchBlt(pOnImage->Canvas->Handle, 0, 0, nOnWidth, nOnHeight,
pImage2->Canvas->Handle, 0, 0, image_width, image_height, SRCPAINT);
pOnImage->Refresh();
}
delete pImage3;
delete pImage2;
delete pImage1;
pOnImage->Canvas->StretchDraw(rect, pImage->Picture->Graphic);
■ 相关文献链接:
● 相关图表链接:
- 图1.1.1 两个人脸之间的图像渐变
- 图1.2.2 两个具有不同笔迹批注的画面
- 图1.2.1 参与测试的前后两张图片
- 图1.2.2 前后两个图像取异或(XOR)
- 图1.2.3 消失与新增的笔画
- 图1.2.4 分别与前面图像和后面图像进行OR, 或者消失与新增画笔
- 图1.2.5 笔画图片块
- 图1.2.6 从前笔画的方块移动到后笔画对应的位置
- 图1.2.7 图片块移动
文章来源: zhuoqing.blog.csdn.net,作者:卓晴,版权归原作者所有,如需转载,请联系作者。
原文链接:zhuoqing.blog.csdn.net/article/details/125109865
- 点赞
- 收藏
- 关注作者
评论(0)