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

📄 main.cpp

📁 // // Histogram Sample // This sample shows how to use the Sample Grabber filter for video image p
💻 CPP
字号:
//
// Histogram Sample
// This sample shows how to use the Sample Grabber filter for video image processing.

// Conceptual background:
// A histogram is just a frequency count of every pixel value in the image.
// There are various well-known mathematical operations that you can perform on an image
// using histograms, to enhance the image, etc. 

// Histogram stretch (aka automatic gain control):
// Stretches the image histogram to fill the entire range of values. This is a "point operation," 
// meaning each pixel is scaled to a new value, without examining the neighboring pixels. The
// histogram stretch does not actually require you to calculate the full histogram. The scaling factor
// is calculated from the minimum and maximum values in the image.

// Histogram equalization: 
// This point operation smooths the histogram curve to bring out details. This is a good way to
// process some images that have a narrow brightness range, eg very dark images. From ad hoc testing 
// on my own video files, however, the histogram stretch usually gives better results and is simpler.

// Logarithmic stretch:
// This point operation applies a logarithm operator to each pixel value, which has the effect
// of boosting the contrast in the darker regions of the image. 

// References: Handbook of Image and Video Processing (Al Bovik, ed. Academic Press)
// There are also a lot of good web sites about image processing.

// Implementation notes:
// This is a console app. It creates a filter graph like this:
//          src -> sample grabber -> avi mux -> file writer
// Video processing happens inside the sample grabber callback.
//
// More notes:
//
// - I encoded the video to a type-2 DV file.
//
// - For simplicitly, I only handle UYVY. This may cause a color conversion filter to be added
//   to the graph. The histogram operations are based on YUV luma values. 
//
// - The sample grabber is an in-place transform. This saves some memory, which is useful in
//   a file-processing scenario, especially if you are applying a series of several transforms
//   to the image. However, if you connect an in-place transform directly to the video renderer, 
//   it is very likely to kill your perf. (You have been warned!) Also, you are writing over the
//   original image, so you can't use this for non-point operations.
//
//   Basically, the sample grabber is intended for getting the bits into your app, manipulating
//   them, and possibly writing the results to a file.

// See also:
// Microsoft Research Vision SDK (http://msdn.microsoft.com/library/en-us/dnvissdk/html/vissdk.asp)
// Provides a set of template-based C++ classes for manipulating RGB and YUV images. 


/////////////////////////

// main.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "Histogram.h"

#include "dsutil.h"


// GrabberCB: Class to implement the callback function for the sample grabber
// Note: No ref-counting, so keep the object in scope while you're using it!
class GrabberCB : public ISampleGrabberCB 
{
private:
    BITMAPINFOHEADER m_bmi;  // Holds the bitmap format 
    bool m_fFirstSample;     // true if the next sample is the first one.


public:

    GrabberCB();
    ~GrabberCB();

    // IUnknown methods
    STDMETHODIMP_(ULONG) AddRef() { return 1; }
    STDMETHODIMP_(ULONG) Release() { return 2; }
    STDMETHOD(QueryInterface)(REFIID iid, void** ppv);

    // ISampleGrabberCB methods
    STDMETHOD(SampleCB)(double SampleTime, IMediaSample *pSample);
    STDMETHODIMP BufferCB(double, BYTE *, long) { return E_NOTIMPL; }

};

HRESULT RunFile(LPTSTR pszSrcFile, LPTSTR pszDestFile);

GrabberCB grabberCallback;  // Make this global so that it stays in scope while the graph exists

// There are three image op types - uncomment the desired one.
// CContrastStretch g_stretch;  
   CEqualize g_stretch;
// CLogStretch g_stretch;

int __cdecl _tmain(int argc, _TCHAR* argv[])
{
    HRESULT hr;

    hr = CoInitialize(0);
    if (FAILED(hr))
    {
        printf("Could not initialize the COM library.\n");
        return -1;
    }

    // For now use a hard-coded file name

    hr = RunFile("C:\\Sunset.avi", "C:\\Histogram_Output.avi");
    if (FAILED(hr))
    {
        printf("Failed to convert file.\n");
    }

    CoUninitialize();
	return 0;
}


// RunFile:
// Load a video file, connect it to the sample grabber, process the video, 
// and write the results to an AVI file.

HRESULT RunFile(LPTSTR pszSrcFile, LPTSTR pszDestFile)
{
    HRESULT hr;
    
    USES_CONVERSION;  // For TCHAR -> WCHAR conversion macros

    CComPtr<IGraphBuilder> pGraph;  // Filter Graph Manager
    CComPtr<IBaseFilter> pGrabF;    // Sample grabber
    CComPtr<IBaseFilter> pMux;      // AVI Mux
    CComPtr<IBaseFilter> pSrc;      // Source filter
    CComPtr<IBaseFilter> pDVEnc;    // DV Encoder

    CComPtr<ICaptureGraphBuilder2> pBuild;

    // Create the filter graph manager.
    hr = pGraph.CoCreateInstance(CLSID_FilterGraph);
    if (FAILED(hr))
    {
        printf("Could not create the Filter Graph Manager (hr = 0x%X.)\n", hr);
        return hr;
    }

    // Create the capture graph builder.
    hr = pBuild.CoCreateInstance(CLSID_CaptureGraphBuilder2);
    if (FAILED(hr))
    {
        printf("Could not create the Capture Graph Builder (hr = 0x%X.)\n", hr);
        return hr;
    }
    pBuild->SetFiltergraph(pGraph);

    // Build the file-writing portion of the graph.
    hr = pBuild->SetOutputFileName(&MEDIASUBTYPE_Avi, T2W(pszDestFile), &pMux, NULL);
    if (FAILED(hr))
    {
        printf("Could not hook up the AVI Mux / File Writer (hr = 0x%X.)\n", hr);
        return hr;
    }

    // Add the source filter for the input file
    hr = pGraph->AddSourceFilter(T2W(pszSrcFile), L"Source", &pSrc);
    if (FAILED(hr))
    {
        printf("Could not add the source filter (hr = 0x%X.)\n", hr);
        return hr;
    }


    // Create some filters and add them to the graph

    // DV Video Encoder
    hr = AddFilterByCLSID(pGraph, CLSID_DVVideoEnc, L"DV Encoder", &pDVEnc);
    if (FAILED(hr))
    {
        printf("Could not add the DV video encoder filter (hr = 0x%X.)\n", hr);
        return hr;
    }
    

    // Sample Grabber
    hr = AddFilterByCLSID(pGraph, CLSID_SampleGrabber, L"Grabber", &pGrabF);
    if (FAILED(hr))
    {
        printf("Could not add the sample grabber filter (hr = 0x%X.)\n", hr);
        return hr;
    }
    CComQIPtr<ISampleGrabber> pGrabber(pGrabF);
    if (!pGrabF)
    {
        return E_NOINTERFACE;
    }

    // Configure the sample grabber
    AM_MEDIA_TYPE mt;
    ZeroMemory(&mt, sizeof(AM_MEDIA_TYPE));
    mt.majortype = MEDIATYPE_Video;
    mt.subtype = MEDIASUBTYPE_UYVY;
	mt.formattype = FORMAT_VideoInfo; 

    // Note: I don't expect the next few methods to fail ....

    hr = pGrabber->SetMediaType(&mt);  // Set the media type we want for the connection.
    _ASSERTE(SUCCEEDED(hr));

    hr = pGrabber->SetOneShot(FALSE);  // Disable "one-shot" mode
    _ASSERTE(SUCCEEDED(hr));

    hr = pGrabber->SetBufferSamples(FALSE); // Disable sample buffering
    _ASSERTE(SUCCEEDED(hr));

    hr = pGrabber->SetCallback(&grabberCallback, 0); // Set our callback. '0' means 'use the SampleCB callback'
    _ASSERTE(SUCCEEDED(hr));


    // Build the graph. First connect the source to the DV Encoder, through the sample grabber.
    // This should connect the video stream.
    hr = pBuild->RenderStream(0, 0, pSrc, pGrabF, pDVEnc);
    if (SUCCEEDED(hr))
    {
        // Next, connect the DV Encoder to the AVI Mux
        hr = pBuild->RenderStream(0, 0, pDVEnc, 0, pMux);

        if (SUCCEEDED(hr))
        {
            // Maybe we have an audio stream. If so, connect it the AVI Mux.
            // But don't fail if we don't...
            HRESULT hrTmp = pBuild->RenderStream(0, 0, pSrc, 0, pMux);

            SaveGraphFile(pGraph, L"C:\\Grabber.grf");
        }
    }

    if (FAILED(hr))
    {
       printf("Error building the graph (hr = 0x%X.)\n", hr);
       return hr;
    }


    // Find out the exact video format.
    hr = pGrabber->GetConnectedMediaType(&mt);
    if (FAILED(hr)) 
    {
        printf("Could not get the video format. (hr = 0x%X.)\n", hr);
        return hr;  
    }
    
    VIDEOINFOHEADER *pVih;
    if ((mt.subtype == MEDIASUBTYPE_UYVY) && (mt.formattype == FORMAT_VideoInfo))
    {
        pVih = reinterpret_cast<VIDEOINFOHEADER*>(mt.pbFormat);
    }
    else 
    {
        // This is not the format we expected!
   		CoTaskMemFree(mt.pbFormat);
        return VFW_E_INVALIDMEDIATYPE;
    }

    g_stretch.SetFormat(*pVih);
    CoTaskMemFree(mt.pbFormat);

    // Turn off the graph clock
    CComQIPtr<IMediaFilter> pMF(pGraph);
    pMF->SetSyncSource(NULL);

    // Run the graph to completion.
    CComQIPtr<IMediaControl> pControl(pGraph);
    CComQIPtr<IMediaEvent> pEvent(pGraph);
    long evCode = 0;

    printf("Processing the video file... ");


    pControl->Run();
    pEvent->WaitForCompletion(INFINITE, &evCode);
    pControl->Stop();

    printf("Done.\n");

    return hr;
}


GrabberCB::GrabberCB() : m_fFirstSample(true)
{
}

GrabberCB::~GrabberCB()
{
}

// Support querying for ISampleGrabberCB interface
HRESULT GrabberCB::QueryInterface(REFIID iid, void**ppv)
{
    if (!ppv) { return E_POINTER; }
    if (iid == IID_IUnknown)
    {
        *ppv = static_cast<IUnknown*>(this);
    }
    else if (iid == IID_ISampleGrabberCB)
    {
        *ppv = static_cast<ISampleGrabberCB*>(this);
    }
    else
    {
        return E_NOINTERFACE;
    }
    AddRef();  // We don't actually ref count, but in case we change the implementation later.
    return S_OK;
}

// SampleCB: This is where we process each sample
HRESULT GrabberCB::SampleCB(double SampleTime, IMediaSample *pSample)
{

    HRESULT hr;

    // Get the pointer to the buffer
    BYTE *pBuffer;
    hr = pSample->GetPointer(&pBuffer);

    // Tell the image processing class about it
    g_stretch.SetImage(pBuffer);

    if (FAILED(hr))
    {
        OutputDebugString(TEXT("SampleCB: GetPointer FAILED\n"));
        return hr;
    }

    // Scan the image on the first sample. Re-scan is there is a discontinuity.
    // (This will produce horrible results if there are big scene changes in the
    // video that are not associated with discontinuities. Might be safer to re-scan
    // each image, at a higher perf cost.)

    if (m_fFirstSample)
    {
        hr = g_stretch.ScanImage();
        m_fFirstSample = false;
    }
    else if (S_OK == pSample->IsDiscontinuity())
    {
        hr = g_stretch.ScanImage();
    }

    // Convert the image
    hr = g_stretch.ConvertImage();


    return S_OK;
}


⌨️ 快捷键说明

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