📄 video.cpp
字号:
bool FindSeed(VideoSmartSelectSeed* pThat)
{
VideoSmartSelectSeed* pSeed;
for(pSeed = m_pSeeds; pSeed; pSeed = pSeed->GetNextSibling())
{
if(pSeed == pThat)
return true;
}
return false;
}
void RemoveSeed(VideoSmartSelectSeed* pSeed)
{
GAssert(FindSeed(pSeed), "Wrong frame for this seed");
GAssert(pSeed != m_pGrabbedSeed, "shouldn't remove a grabbed seed");
int nFramesFromOrigin = pSeed->GetFramesFromOrigin();
VideoSmartSelectSeed* pNextSeed = pSeed->GetNext();
VideoSmartSelectSeed* pPrevSeed = pSeed->GetPrev();
VideoSmartSelectSeed* pNewHead = pSeed->Unlink(m_pSeeds);
GAssert(pNewHead == m_pSeeds || pSeed == m_pSeeds, "head problem");
m_pSeeds = pNewHead;
if(nFramesFromOrigin >= 0 && pNextSeed)
{
GAssert(pNextSeed->GetFramesFromOrigin() > nFramesFromOrigin, "out of order");
m_pNext->RemoveSeed(pNextSeed);
}
if(nFramesFromOrigin <= 0 && pPrevSeed)
{
GAssert(pPrevSeed->GetFramesFromOrigin() < nFramesFromOrigin, "out of order");
m_pPrev->RemoveSeed(pPrevSeed);
}
GAssert(!pSeed->GetNextSibling(), "Not unlinked properly");
delete(pSeed);
UpdateSelection();
}
void ComputeNextFrameSeedPos(int x, int y, int* pOutX, int* pOutY, VideoSmartSelectFrame* pTargetFrame)
{
/*
*pOutX = x;
*pOutY = y;
*/
GAssert(m_pImage->GetWidth() >= SUB_IMAGE_SIZE && m_pImage->GetHeight() >= SUB_IMAGE_SIZE, "Not enough room for the sub-image");
GRect r(x - SUB_IMAGE_SIZE / 2, y - SUB_IMAGE_SIZE / 2, SUB_IMAGE_SIZE, SUB_IMAGE_SIZE);
if(r.x < 0)
r.x = 0;
if(r.y < 0)
r.y = 0;
if(r.x + r.w >= m_pImage->GetWidth())
r.x = m_pImage->GetWidth() - r.w;
if(r.y + r.h >= m_pImage->GetHeight())
r.y = m_pImage->GetHeight() - r.h;
GRect r2(r.x - SUB_IMAGE_SIZE / 8, r.y - SUB_IMAGE_SIZE / 8, SUB_IMAGE_SIZE / 4, SUB_IMAGE_SIZE / 4);
GImage tmp;
tmp.CopyImage(m_pImage);
tmp.QuickBlur(7);
pTargetFrame->m_pFinder->FindSubImage(pOutX, pOutY, &tmp, &r, &r2);
(*pOutX) += (x - r.x);
(*pOutY) += (y - r.y);
if(*pOutX < 0)
*pOutX = 0;
if(*pOutY < 0)
*pOutY = 0;
if(*pOutX >= m_pImage->GetWidth())
*pOutX = m_pImage->GetWidth() - 1;
if(*pOutY >= m_pImage->GetHeight())
*pOutY = m_pImage->GetHeight() - 1;
}
bool IsSelected(int x, int y)
{
GColor c = m_pSelection->GetPixel(x, y);
if(gAlpha(c) > 0 && c != SINK_SEED_COLOR)
return true;
else
return false;
}
bool GetSampleSelectedPoint(int* pX, int* pY)
{
VideoSmartSelectSeed* pSeed;
for(pSeed = m_pSeeds; pSeed; pSeed = pSeed->GetNextSibling())
{
if(pSeed->IsSource())
{
*pX = pSeed->GetX();
*pY = pSeed->GetY();
return true;
}
}
return false;
}
void ComputeLocationWithinSelection(int* pWid, int* pHgt, float* pOutH, float* pOutV, VideoSmartSelectSeed* pSeed)
{
int x = pSeed->GetX();
int y = pSeed->GetY();
bool s = IsSelected(x, y);
// Find the four distances to the edge of the selection
int l, r, t, b;
for(l = x; l > 0 && IsSelected(l - 1, y) == s; l--) {}
for(r = x; r < m_pSelection->GetWidth() - 1 && IsSelected(r + 1, y) == s; r++) {}
for(t = y; t > 0 && IsSelected(x, t - 1) == s; t--) {}
for(b = y; b < m_pSelection->GetHeight() - 1 && IsSelected(x, b + 1) == s; b++) {}
*pOutH = (float)(x - l) / (r - l + 1);
*pOutV = (float)(y - t) / (b - t + 1);
*pWid = r - l + 1;
*pHgt = b - t + 1;
}
bool UpdateSeedPosition(VideoSmartSelectSeed* pSeed, int wid, int hgt, float h, float v)
{
GImage* pRegionMask = m_pRegions->GetRegionMask();
int x = pSeed->GetX();
int y = pSeed->GetY();
bool s = IsSelected(x, y);
// Find the four distances to the edge of the selection
int l, r, t, b;
for(l = x; l > 0 && IsSelected(l - 1, y) == s; l--) {}
for(r = x; r < m_pSelection->GetWidth() - 1 && IsSelected(r + 1, y) == s; r++) {}
for(t = y; t > 0 && IsSelected(x, t - 1) == s; t--) {}
for(b = y; b < m_pSelection->GetHeight() - 1 && IsSelected(x, b + 1) == s; b++) {}
// Do sanity checks
float fWidRatio = (float)wid / (r - l + 1);
if(fWidRatio > (float)1.0)
fWidRatio = (float)1 / fWidRatio;
if(fWidRatio < (float).65)
return false;
float fHgtRatio = (float)hgt / (b - t + 1);
if(fHgtRatio > (float)1.0)
fHgtRatio = (float)1 / fHgtRatio;
if(fHgtRatio < (float).65)
return false;
// Move the seed
int nNewX = l + (int)(h * (r - l + 1) + .5);
int nNewY = t + (int)(v * (b - t + 1) + .5);
if(nNewX == x && nNewY == y)
return false;
pSeed->SetPos(nNewX, nNewY, pRegionMask->GetPixel(nNewX, nNewY));
return true;
}
void PropagateSeeds()
{
float h, v;
int wid, hgt;
VideoSmartSelectSeed* pSeed;
for(pSeed = m_pSeeds; pSeed; pSeed = pSeed->GetNextSibling())
{
if(!pSeed->GetPrev())
{
ComputeLocationWithinSelection(&wid, &hgt, &h, &v, pSeed);
Propagate(pSeed, false, wid, hgt, h, v);
}
if(!pSeed->GetNext())
{
ComputeLocationWithinSelection(&wid, &hgt, &h, &v, pSeed);
Propagate(pSeed, true, wid, hgt, h, v);
}
}
}
void Propagate(VideoSmartSelectSeed* pSeed, bool bRight, int wid, int hgt, float h, float v)
{
int x, y, nRegion;
VideoSmartSelectFrame* pTargetFrame = (bRight ? m_pNext : m_pPrev);
while(pTargetFrame)
{
// Estimate the initial position with image correlation
ComputeNextFrameSeedPos(pSeed->GetX(), pSeed->GetY(), &x, &y, pTargetFrame);
// Make a new seed
GImage* pRegionMask = pTargetFrame->m_pRegions->GetRegionMask();
nRegion = pRegionMask->GetPixel(x, y);
VideoSmartSelectSeed* pNewSeed = new VideoSmartSelectSeed(pSeed->IsSource(), x, y, nRegion, pSeed->GetFramesFromOrigin() + (bRight ? 1 : -1));
pNewSeed->LinkAtHead(pTargetFrame->m_pSeeds, bRight ? pSeed : NULL, bRight ? NULL : pSeed);
pTargetFrame->m_pSeeds = pNewSeed;
// Converge to new position with graph cut
int nSafety = 6;
while(--nSafety >= 0)
{
pTargetFrame->UpdateSelection();
if(!pTargetFrame->UpdateSeedPosition(pNewSeed, wid, hgt, h, v))
break;
}
// Move on to the next frame
pSeed = pNewSeed;
pTargetFrame = (bRight ? pTargetFrame->m_pNext : pTargetFrame->m_pPrev);
}
}
void SanityCheckSeeds()
{
VideoSmartSelectSeed* pPrevSib = NULL;
VideoSmartSelectSeed* pSeed;
VideoSmartSelectSeed* pSeed2;
VideoSmartSelectSeed* pSeed3;
for(pSeed = m_pSeeds; pSeed; pSeed = pSeed->GetNextSibling())
{
GAssert(pSeed->GetPrevSibling() == pPrevSib, "sibling list corrupt");
pSeed3 = pSeed;
for(pSeed2 = pSeed->GetPrev(); pSeed2; pSeed2 = pSeed2->GetPrev())
{
GAssert(pSeed2->GetNext() == pSeed3, "next chain corrupt");
GAssert(pSeed2->GetFramesFromOrigin() < pSeed->GetFramesFromOrigin(), "wrong order");
pSeed3 = pSeed2;
}
pSeed3 = pSeed;
for(pSeed2 = pSeed->GetNext(); pSeed2; pSeed2 = pSeed2->GetNext())
{
GAssert(pSeed2->GetPrev() == pSeed3, "prev chain corrupt");
GAssert(pSeed2->GetFramesFromOrigin() > pSeed->GetFramesFromOrigin(), "wrong order");
pSeed3 = pSeed2;
}
pPrevSib = pSeed;
}
}
void OnMouseDown(int x, int y, bool bSource)
{
GImage* pRegionMask = m_pRegions->GetRegionMask();
if(x < 0 || y < 0 || x >= pRegionMask->GetWidth() || y >= pRegionMask->GetHeight())
return;
int nRegion = pRegionMask->GetPixel(x, y);
VideoSmartSelectSeed* pSeed = FindSeed(nRegion);
m_pGrabbedSeed = NULL;
if(pSeed)
{
if(pSeed->IsSource() == bSource)
m_pGrabbedSeed = pSeed;
else
{
RemoveSeed(pSeed);
}
}
else
{
// Make the new seed
VideoSmartSelectSeed* pNewSeed = new VideoSmartSelectSeed(bSource, x, y, nRegion, 0);
pNewSeed->LinkAtHead(m_pSeeds, NULL, NULL);
m_pSeeds = pNewSeed;
}
}
void OnMouseUp(int x, int y)
{
if(!m_pGrabbedSeed)
return;
GImage* pRegionMask = m_pRegions->GetRegionMask();
if(x >= 0 && x < pRegionMask->GetWidth() && y >= 0 && y < pRegionMask->GetHeight())
{
if(!FindSeed(m_pGrabbedSeed))
{
GAssert(false, "grabbed seed in another frame");
m_pGrabbedSeed = NULL;
return;
}
int nNewRegion = pRegionMask->GetPixel(x, y);
if(nNewRegion != m_pGrabbedSeed->GetRegion())
{
m_pGrabbedSeed->SetPos(x, y, nNewRegion);
if(m_pGrabbedSeed->GetFramesFromOrigin() >= 0 && m_pGrabbedSeed->GetNext())
m_pNext->RemoveSeed(m_pGrabbedSeed->GetNext());
if(m_pGrabbedSeed->GetFramesFromOrigin() <= 0 && m_pGrabbedSeed->GetPrev())
m_pPrev->RemoveSeed(m_pGrabbedSeed->GetPrev());
UpdateSelection();
}
}
m_pGrabbedSeed = NULL;
}
};
class VideoToolSmartSelect : public VideoTool
{
protected:
VideoSmartSelectFrame* m_pFrames1;
VideoSmartSelectFrame* m_pFrames2;
VideoSmartSelectFrame* m_pFrames;
int m_nCurrentButton;
GVideo* m_pVideo1;
GVideo* m_pVideo2;
GVideo* m_pSelection1;
GVideo* m_pSelection2;
int m_nFrame;
public:
VideoToolSmartSelect(VideoController* pController) : VideoTool(pController)
{
m_pFrames1 = NULL;
m_pFrames2 = NULL;
m_pFrames = NULL;
m_nFrame = -1;
m_pVideo1 = NULL;
m_pVideo2 = NULL;
m_pSelection1 = NULL;
m_pSelection2 = NULL;
}
virtual ~VideoToolSmartSelect()
{
delete[] m_pFrames1;
delete[] m_pFrames2;
}
virtual void OnSelect()
{
m_pVideo1 = m_pController->GetVideo1();
m_pVideo2 = m_pController->GetVideo2();
m_pSelection1 = m_pController->GetSelection1();
m_pSelection2 = m_pController->GetSelection2();
delete[] m_pFrames1;
delete[] m_pFrames2;
m_pFrames1 = new VideoSmartSelectFrame[m_pVideo1->GetFrameCount()];
m_pFrames2 = new VideoSmartSelectFrame[m_pVideo2->GetFrameCount()];
int i;
for(i = 0; i < m_pVideo1->GetFrameCount(); i++)
m_pFrames1[i].MakeRegions(m_pVideo1->GetFrame(i), m_pSelection1->GetFrame(i), i > 0 ? &m_pFrames1[i - 1] : NULL, i < m_pVideo1->GetFrameCount() - 1 ? &m_pFrames1[i + 1] : NULL);
for(i = 0; i < m_pVideo2->GetFrameCount(); i++)
m_pFrames2[i].MakeRegions(m_pVideo2->GetFrame(i), m_pSelection2->GetFrame(i), i > 0 ? &m_pFrames2[i - 1] : NULL, i < m_pVideo2->GetFrameCount() - 1 ? &m_pFrames2[i + 1] : NULL);
}
virtual void OnMouseDown(int nFrame, GVideo* pVideo, GVideo* pSelection, int nButton, int x, int y)
{
if(pVideo == m_pVideo1)
m_pFrames = m_pFrames1;
else if(pVideo == m_pVideo2)
m_pFrames = m_pFrames2;
else
GAssert(false, "unrecognized video");
m_nFrame = nFrame;
m_nCurrentButton = nButton;
bool bSource;
if(nButton == 4)
{
m_pController->OnScrollWheel(false);
return;
}
else if(nButton == 5)
{
m_pController->OnScrollWheel(true);
return;
}
else if(nButton == 9 || nButton == 2) // middle button
{
m_pFrames[m_nFrame].PropagateSeeds();
return;
}
else if(nButton == 1) // left button
bSource = true;
else if(nButton == 3) // right button
bSource = false;
else
return;
m_pFrames[m_nFrame].OnMouseDown(x, y, bSource);
m_pFrames[m_nFrame].UpdateSelection();
}
virtual void OnMouseUp(int nButton, int x, int y)
{
if(nButton != 1 && nButton != 3)
return;
m_pFrames[m_nFrame].OnMouseUp(x, y);
}
/*
virtual void OnMouseMove(int x, int y, bool bPressed)
{
}
*/
static GColor BlendColors(GColor a, GColor b, float fac)
{
float f2 = (float)1.0 - fac;
return gARGB(
0xff,
(int)(gRed(a) * f2 + gRed(b) * fac),
(int)(gGreen(a) * f2 + gGreen(b) * fac),
(int)(gBlue(a) * f2 + gBlue(b) * fac)
);
}
void LoadSeeds()
{
const char* szError;
int nLine;
GXMLTag* pTagRoot = GXMLTag::FromFile("seeds.xml", &szError, NULL, &nLine, NULL);
Holder<GXMLTag*> hTagRoot(pTagRoot);
GAssert(pTagRoot, "Failed to load");
// Load video 1
GXMLTag* pTagSeed;
int nFrame;
GXMLTag* pTagVideo1 = pTagRoot->GetChildTag("Video1");
GAssert(pTagVideo1, "Expected a <Video1> tag");
for(pTagSeed = pTagVideo1->GetFirstChildTag(); pTagSeed; pTagSeed = pTagVideo1->GetNextChildTag(pTagSeed))
{
GXMLAttribute* pFrameAttr = pTagSeed->GetAttribute("Frame");
nFrame = atoi(pFrameAttr->GetValue());
m_pFrames1[nFrame].LoadSeeds(pTagSeed);
}
// Load video 2
GXMLTag* pTagVideo2 = pTagRoot->GetChildTag("Video2");
GAssert(pTagVideo2, "Expected a <Video2> tag");
for(pTagSeed = pTagVideo2->GetFirstChildTag(); pTagSeed; pTagSeed = pTagVideo2->GetNextChildTag(pTagSeed))
{
GXMLAttribute* pFrameAttr = pTagSeed->GetAttribute("Frame");
nFrame = atoi(pFrameAttr->GetValue());
m_pFrames2[nFrame].LoadSeeds(pTagSeed);
}
// Update all the selections
int i;
for(i = 0; i < m_pVideo1->GetFrameCount(); i++)
m_pFrames1[i].UpdateSelection();
for(i = 0; i < m_pVideo2->GetFrameCount(); i++)
m_pFrames2[i].UpdateSelection();
}
void SaveSeeds()
{
int i;
GXMLTag root("Seeds");
GXMLTag* pTagVideo1 = new GXMLTag("Video1");
root.AddChildTag(pTagVideo1);
for(i = 0; i < m_pVideo1->GetFrameCount(); i++)
m_pFrames1[i].SaveSeeds(pTagVideo1, i);
GXMLTag* pTagVideo2 = new GXMLTag("Video2");
root.AddChildTag(pTagVideo2);
for(i = 0; i < m_pVideo2->GetFrameCount(); i++)
m_pFrames2[i].SaveSeeds(pTagVideo2, i);
bool bOK = root.ToFile("seeds.xml");
GAssert(bOK, "failed to save");
}
void Morph()
{
#ifdef WIN32
mkdir("output");
#else // WIN32
mkdir("output", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
#endif // !WIN32
chdir("output");
// Make a blank video of the right size
printf("Allocating space for the video...\n");
GAssert(m_pVideo1->GetWidth() == m_pVideo2->GetWidth() && m_pVideo1->GetHeight() == m_pVideo2->GetHeight(), "videos are different sizes");
GVideo morphVideo(m_pVideo1->GetWidth(), m_pVideo1->GetHeight());
int nFrames = MIN(m_pVideo1->GetFrameCount(), m_pVideo2->GetFrameCount());
int i;
for(i = 0; i < nFrames; i++)
morphVideo.AddBlankFrame();
// Change source seed selection areas to selection color
printf("Changing selection color...\n");
int x, y, z;
GImage* pSel1;
GImage* pSel2;
for(z = 0; z < nFrames; z++)
{
pSel1 = m_pSelection1->GetFrame(z);
pSel2 = m_pSelection2->GetFrame(z);
for(y = 0; y < morphVideo.GetHeight(); y++)
{
for(x = 0; x < morphVideo.GetWidth(); x++)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -