📄 video.cpp
字号:
// --------------------------------------------------------
// This demo file is dedicated to the Public Domain. See:
// http://creativecommons.org/licenses/publicdomain
// --------------------------------------------------------
#include "Video.h"
#ifdef WIN32
#include <direct.h>
#else // WIN32
#include <unistd.h>
#endif // !WIN32
#include "../GClasses/GBitTable.h"
#include "../GClasses/GTime.h"
#include "../GClasses/GMacros.h"
#include "../GClasses/GFile.h"
#include "../GClasses/GImage.h"
#include "../GClasses/GVideo.h"
#include "../GClasses/GBits.h"
#include "../GClasses/GPointerQueue.h"
#include "../GClasses/GThread.h"
#include "../GClasses/GRegion.h"
#include "../GClasses/GGraph.h"
#include "../GClasses/GKNN.h"
#include "../GClasses/GBezier.h"
#include "../GClasses/GArff.h"
#include "../GClasses/GCluster.h"
#include "../GClasses/GMorph.h"
#include "../GClasses/GXML.h"
#include "Interpolate.h"
#include <math.h>
#define VIDEO_TAB_COUNT 3
#define TOOL_AREA_SIZE 150
#define VIDEO_AREA_BACKGROUND_COLOR 0xff888888
class VideoTool
{
protected:
VideoController* m_pController;
public:
VideoTool(VideoController* pController)
{
m_pController = pController;
}
virtual ~VideoTool() {}
virtual void OnSelect() {}
virtual void OnMouseDown(int nFrame, GVideo* pVideo, GVideo* pSelection, int nButton, int x, int y) = 0;
virtual void OnMouseUp(int nButton, int x, int y) {}
virtual void OnMouseMove(int x, int y, bool bPressed) {}
};
class VideoToolPen : public VideoTool
{
protected:
int m_prevX, m_prevY, m_nFrame;
GVideo* m_pVideo;
public:
VideoToolPen(VideoController* pController) : VideoTool(pController)
{
m_nFrame = -1;
m_pVideo = NULL;
}
virtual ~VideoToolPen() {}
virtual void OnMouseDown(int nFrame, GVideo* pVideo, GVideo* pSelection, int nButton, int x, int y)
{
m_pVideo = pVideo;
m_nFrame = nFrame;
m_prevX = x;
m_prevY = y;
pVideo->GetFrame(nFrame)->SafeSetPixel(x, y, 0xffff0000);
}
virtual void OnMouseMove(int x, int y, bool bPressed)
{
if(bPressed && (x != m_prevX || y != m_prevY))
{
m_pVideo->GetFrame(m_nFrame)->SafeDrawLine(m_prevX, m_prevY, x, y, 0xffff0000);
m_prevX = x;
m_prevY = y;
}
}
};
class VideoSmartSelectSeed
{
protected:
VideoSmartSelectSeed* m_pNext;
VideoSmartSelectSeed* m_pPrev;
VideoSmartSelectSeed* m_pSibNext;
VideoSmartSelectSeed* m_pSibPrev;
bool m_bSource;
int m_nRegion;
int m_nFramesFromOrigin;
int m_x, m_y;
public:
VideoSmartSelectSeed(bool bSource, int x, int y, int nRegion, int nFramesFromOrigin)
{
m_pNext = NULL;
m_pPrev = NULL;
m_pSibNext = NULL;
m_pSibPrev = NULL;
m_bSource = bSource;
m_x = x;
m_y = y;
m_nRegion = nRegion;
m_nFramesFromOrigin = nFramesFromOrigin;
}
~VideoSmartSelectSeed()
{
VideoSmartSelectSeed* pCondemned;
while(m_pSibNext)
{
pCondemned = m_pSibNext;
m_pSibNext = pCondemned->m_pSibNext;
pCondemned->m_pSibNext = NULL;
delete(pCondemned);
}
}
void ToXml(GXMLTag* pTag)
{
char szTmp[32];
itoa(m_x, szTmp, 10);
pTag->AddAttribute(new GXMLAttribute("x", szTmp));
itoa(m_y, szTmp, 10);
pTag->AddAttribute(new GXMLAttribute("y", szTmp));
pTag->AddAttribute(new GXMLAttribute("source", m_bSource ? "true" : "false"));
}
void SaveSeedChain(GXMLTag* pTagVideo, int nFrame)
{
// Save the root seed
GXMLTag* pSeedTag = new GXMLTag("Seed");
pTagVideo->AddChildTag(pSeedTag);
char szTmp[32];
itoa(nFrame, szTmp, 10);
pSeedTag->AddAttribute(new GXMLAttribute("Frame", szTmp));
ToXml(pSeedTag);
// Save the prev chain
GXMLTag* pTagPrev = new GXMLTag("Prev");
pSeedTag->AddChildTag(pTagPrev);
VideoSmartSelectSeed* pSeed;
for(pSeed = GetPrev(); pSeed; pSeed = pSeed->GetPrev())
{
GXMLTag* pNewSeedTag = new GXMLTag("Seed");
pTagPrev->AddChildTag(pNewSeedTag);
pSeed->ToXml(pNewSeedTag);
}
// Save the next chain
GXMLTag* pTagNext = new GXMLTag("Next");
pSeedTag->AddChildTag(pTagNext);
for(pSeed = GetNext(); pSeed; pSeed = pSeed->GetNext())
{
GXMLTag* pNewSeedTag = new GXMLTag("Seed");
pTagNext->AddChildTag(pNewSeedTag);
pSeed->ToXml(pNewSeedTag);
}
}
void SetPos(int x, int y, int nRegion)
{
m_x = x;
m_y = y;
m_nRegion = nRegion;
}
VideoSmartSelectSeed* GetNextSibling()
{
return m_pSibNext;
}
VideoSmartSelectSeed* GetPrevSibling()
{
return m_pSibPrev;
}
VideoSmartSelectSeed* GetNext()
{
return m_pNext;
}
VideoSmartSelectSeed* GetPrev()
{
return m_pPrev;
}
bool IsSource()
{
return m_bSource;
}
int GetFramesFromOrigin()
{
return m_nFramesFromOrigin;
}
int GetRegion()
{
return m_nRegion;
}
int GetX()
{
return m_x;
}
int GetY()
{
return m_y;
}
void LinkAtHead(VideoSmartSelectSeed* pOldHead, VideoSmartSelectSeed* pPrev, VideoSmartSelectSeed* pNext)
{
m_pSibNext = pOldHead;
m_pSibPrev = NULL;
if(pOldHead)
{
GAssert(!pOldHead->m_pSibPrev, "not really the head");
pOldHead->m_pSibPrev = this;
}
m_pPrev = pPrev;
if(pPrev)
{
GAssert(pPrev->m_nFramesFromOrigin < m_nFramesFromOrigin, "doesn't precede this seed");
GAssert(!pPrev->m_pNext, "already has a next");
pPrev->m_pNext = this;
}
m_pNext = pNext;
if(pNext)
{
GAssert(pNext->m_nFramesFromOrigin > m_nFramesFromOrigin, "doesn't come after this seed");
GAssert(!pNext->m_pPrev, "already has a prev");
pNext->m_pPrev = this;
}
}
VideoSmartSelectSeed* Unlink(VideoSmartSelectSeed* pOldFirst)
{
if(m_pPrev)
m_pPrev->m_pNext = NULL;
if(m_pNext)
m_pNext->m_pPrev = NULL;
m_pPrev = NULL;
m_pNext = NULL;
if(m_pSibPrev)
{
GAssert(m_pSibPrev->m_pSibNext == this, "problem");
m_pSibPrev->m_pSibNext = m_pSibNext;
}
else
pOldFirst = m_pSibNext;
if(m_pSibNext)
{
GAssert(m_pSibNext->m_pSibPrev == this, "problem");
m_pSibNext->m_pSibPrev = m_pSibPrev;
}
m_pSibNext = NULL;
m_pSibPrev = NULL;
return pOldFirst;
}
};
#define SUB_IMAGE_SIZE 64
#define SOURCE_SEED_COLOR 0xff00ff00 // green
#define SINK_SEED_COLOR 0xffff0000 // red
#define SELECTED_COLOR 0xff8888ff // light purpley-blue
class VideoSmartSelectFrame
{
protected:
VideoSmartSelectSeed* m_pSeeds;
VideoSmartSelectSeed* m_pGrabbedSeed;
G2DRegionGraph* m_pRegions;
GImage* m_pImage;
GImage* m_pSelection;
VideoSmartSelectFrame* m_pNext;
VideoSmartSelectFrame* m_pPrev;
GSubImageFinder* m_pFinder;
public:
VideoSmartSelectFrame()
{
m_pSeeds = NULL;
m_pGrabbedSeed = NULL;
m_pRegions = NULL;
m_pImage = NULL;
m_pSelection = NULL;
m_pNext = NULL;
m_pPrev = NULL;
m_pFinder = NULL;
}
~VideoSmartSelectFrame()
{
delete(m_pSeeds);
delete(m_pFinder);
}
static void ParseSeedTag(GXMLTag* pTagSeed, bool* pSource, int* pX, int* pY)
{
GXMLAttribute* pAttrSource = pTagSeed->GetAttribute("source");
GXMLAttribute* pAttrX = pTagSeed->GetAttribute("x");
GXMLAttribute* pAttrY = pTagSeed->GetAttribute("y");
*pX = atoi(pAttrX->GetValue());
*pY = atoi(pAttrY->GetValue());
*pSource = (stricmp(pAttrSource->GetValue(), "true") == 0);
}
void LoadSeeds(GXMLTag* pTagSeed)
{
int x, y;
bool bSource;
// Make the new seed
ParseSeedTag(pTagSeed, &bSource, &x, &y);
int nRegion = m_pRegions->GetRegionMask()->GetPixel(x, y);
VideoSmartSelectSeed* pNewSeed = new VideoSmartSelectSeed(bSource, x, y, nRegion, 0);
pNewSeed->LinkAtHead(m_pSeeds, NULL, NULL);
m_pSeeds = pNewSeed;
// Load the Prev seeds
GXMLTag* pTagPrev = pTagSeed->GetChildTag("Prev");
GXMLTag* pTagChild;
VideoSmartSelectSeed* pNeighbor = pNewSeed;
VideoSmartSelectSeed* pChildSeed;
VideoSmartSelectFrame* pFrame = m_pPrev;
int nOffset = -1;
for(pTagChild = pTagPrev->GetFirstChildTag(); pTagChild; pTagChild = pTagPrev->GetNextChildTag(pTagChild))
{
ParseSeedTag(pTagChild, &bSource, &x, &y);
nRegion = pFrame->m_pRegions->GetRegionMask()->GetPixel(x, y);
pChildSeed = new VideoSmartSelectSeed(bSource, x, y, nRegion, nOffset);
pChildSeed->LinkAtHead(pFrame->m_pSeeds, NULL, pNeighbor);
pFrame->m_pSeeds = pChildSeed;
pFrame = pFrame->m_pPrev;
pNeighbor = pChildSeed;
nOffset--;
}
// Load the Next seeds
GXMLTag* pTagNext = pTagSeed->GetChildTag("Next");
pNeighbor = pNewSeed;
pFrame = m_pNext;
nOffset = 1;
for(pTagChild = pTagNext->GetFirstChildTag(); pTagChild; pTagChild = pTagNext->GetNextChildTag(pTagChild))
{
ParseSeedTag(pTagChild, &bSource, &x, &y);
nRegion = pFrame->m_pRegions->GetRegionMask()->GetPixel(x, y);
pChildSeed = new VideoSmartSelectSeed(bSource, x, y, nRegion, nOffset);
pChildSeed->LinkAtHead(pFrame->m_pSeeds, pNeighbor, NULL);
pFrame->m_pSeeds = pChildSeed;
pFrame = pFrame->m_pNext;
pNeighbor = pChildSeed;
nOffset++;
}
}
void SaveSeeds(GXMLTag* pTagVideo, int nFrame)
{
VideoSmartSelectSeed* pSeed;
for(pSeed = m_pSeeds; pSeed; pSeed = pSeed->GetNextSibling())
{
if(pSeed->GetFramesFromOrigin() != 0)
continue;
pSeed->SaveSeedChain(pTagVideo, nFrame);
}
}
void ConnectCornersToSink(GGraphCut* pGC, float fAmount)
{
GImage* pRegionMask = m_pRegions->GetRegionMask();
pGC->AddEdge(pRegionMask->GetPixel(0, 0), m_pRegions->GetRegionCount() + 1, fAmount);
pGC->AddEdge(pRegionMask->GetPixel(pRegionMask->GetWidth() - 1, 0), m_pRegions->GetRegionCount() + 1, fAmount);
pGC->AddEdge(pRegionMask->GetPixel(0, pRegionMask->GetHeight() - 1), m_pRegions->GetRegionCount() + 1, fAmount);
pGC->AddEdge(pRegionMask->GetPixel(pRegionMask->GetWidth() - 1, pRegionMask->GetHeight() - 1), m_pRegions->GetRegionCount() + 1, fAmount);
}
void MakeRegions(GImage* pImage, GImage* pSelection, VideoSmartSelectFrame* pPrev, VideoSmartSelectFrame* pNext)
{
GAssert(!m_pRegions && !m_pSeeds && !m_pFinder, "already made");
m_pImage = pImage;
m_pSelection = pSelection;
m_pNext = pNext;
m_pPrev = pPrev;
m_pRegions = new G2DRegionGraph(pImage->GetWidth(), pImage->GetHeight());
m_pRegions->MakeWatershedRegions(pImage);
while(m_pRegions->GetRegionCount() > 2500)
{
G2DRegionGraph* pNewRegionList = new G2DRegionGraph(pImage->GetWidth(), pImage->GetHeight());
pNewRegionList->MakeCoarserRegions(m_pRegions);
delete(m_pRegions);
m_pRegions = pNewRegionList;
}
GImage tmp;
tmp.CopyImage(pImage);
tmp.QuickBlur(7);
m_pFinder = new GSubImageFinder(pImage);
}
void UpdateSelection()
{
// Make a graph of the regions
int nRegionCount = m_pRegions->GetRegionCount();
GGraphCut gc(nRegionCount + 2);
gc.GetEdgesFromRegionList(m_pRegions);
// Weakly connect corners to background
ConnectCornersToSink(&gc, 30);
// Strongly connect seeds to the source orsink
VideoSmartSelectSeed* pSeed;
for(pSeed = m_pSeeds; pSeed; pSeed = pSeed->GetNextSibling())
{
if(pSeed->IsSource())
gc.AddEdge(pSeed->GetRegion(), nRegionCount, (float)1e30); // nRegionCount = the source node
else
gc.AddEdge(pSeed->GetRegion(), nRegionCount + 1, (float)1e30); // nRegionCount + 1 = the sink node
}
// Cut the graph
gc.Cut(nRegionCount, nRegionCount + 1);
// Select the foreground
int x, y;
GImage* pRegionMask = m_pRegions->GetRegionMask();
int nRegion;
for(y = 0; y < m_pSelection->GetHeight(); y++)
{
for(x = 0; x < m_pSelection->GetWidth(); x++)
{
nRegion = pRegionMask->GetPixel(x, y);
if(gc.IsSource(nRegion))
m_pSelection->SetPixel(x, y, SELECTED_COLOR);
else
m_pSelection->SetPixel(x, y, 0);
}
}
// Select the seeds with a special color
GColor c;
for(pSeed = m_pSeeds; pSeed; pSeed = pSeed->GetNextSibling())
{
c = pSeed->IsSource() ? SOURCE_SEED_COLOR : SINK_SEED_COLOR;
GRegionAreaIterator itt(pRegionMask, pSeed->GetX(), pSeed->GetY());
while(itt.GetNext(&x, &y))
m_pSelection->SetPixel(x, y, c);
}
}
VideoSmartSelectSeed* FindSeed(int nRegion)
{
VideoSmartSelectSeed* pSeed;
for(pSeed = m_pSeeds; pSeed; pSeed = pSeed->GetNextSibling())
{
if(pSeed->GetRegion() == nRegion)
return pSeed;
}
return NULL;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -