|
|||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
This article is in the Product Showcase section for our sponsors at The Code Project. These reviews are intended to provide you with information on products and services that we consider useful and of value to developers.
This is a showcase review for our sponsors at CodeProject. These reviews are intended to provide you with information on products and services that we consider useful and of value to developers. AbstractAtalasoft DotImage includes many tools for manipulating or analyzing images. Included in the suite is a class called In batch image processing or in image manipulation applications, it is often necessary to work with large quantities of images. While it is possible to write code to manage these for specific cases, it is often more convenient to be able to solve this problem in the more general case and then use that as a basis for more specific cases. In DotImage, the abstract class
In this model, an image can be thought of as a resource. Rather than simply being read and used, an image is acquired from the In this way, an public void ProcessImages(ImageSource source)
{
while (source.HasMoreImages()) {
AtalaImage image = source.AcquireNext();
ProcessImage(image);
source.Release(image);
}
}
An image that has been acquired and not yet released can be acquired any number of times. In the example above, all the images within the public void ProcessImages(ImageSource source)
{
while (source.HasMoreImages()) {
AtalaImage image = source.AcquireNext();
CreateImageWorker(source, image, ProcessImage);
source.Release(image);
}
}
private void ProcessImage(ImageSource source, AtalaImage image)
{
// do processing here
source.Release(image);
}
public delegate void ProcessImageProc(ImageSource source,
AtalaImage image);
public void CreateImageWorker(ImageSource source,
AtalaImage image, ProcessImageProc proc)
{
source.Acquire(image); // Acquire here
Thread t = CreateImageWorkerThread(source, image, proc);
t.Start();
}
private Thread CreateImageWorkerThread(ImageSource source,
AtalaImage image, ProcessImageProc proc)
{
// threading details left out
}
In this code, the main loop acquires each image, passes it to
For example, this code will always work: TryOne(ImageSource source)
{
source.Reset();
AtalaImage image = source.AcquireNext();
AtalaImage image1 = source.Acquire(0); // reacquire the 0th image
}
If This code will work in most cases: TryTwo(ImageSource source)
{
source.Reset();
AtalaImage image = source.AcquireNext();
source.Release(image);
AtalaImage image1 = source.Acquire(0); // reacquire the 0th image
}
This code will only reliably work if the particular TryThree(ImageSource source)
{
source.Reset();
while (source.HasMoreImages()) {
AtalaImage image = source.AcquireNext();
source.Release(image);
}
// reacquire the 0th image
AtalaImage image1 = source.Acquire(0);
}
The ability to reload an image is not defined within Since not every
public void ProcessImages(RandomAccessImageSource source)
{
for (int i=0; i < source.Count; i++) {
AtalaImage image = source[i]; // this does the acquire
ProcessImage(image);
source.Release(image);
}
}
From here, it is a short step to get to the main concrete For better or for worse, the pattern matching is limited to that provided by .NET for files, which is not full regular expression matching. On one hand, it is consistent with the general Windows User Interface, but on the other hand, it is somewhat limited. To avoid that inherent limitation, yet maintain compatibility, bool MyFilter(string path, int frameIndex, int frameCount)
{
}
a client is able to allow or disallow any file based on its own criteria. By returning To implement a custom protected abstract ImageSourceNode LowLevelAcquireNextImage();
protected abstract bool LowLevelHasMoreImages();
protected abstract void LowLevelReset();
protected abstract void LowLevelSkipNextImage();
protected abstract void LowLevelDispose();
protected abstract bool LowLevelFlushOnReset();
protected abstract bool LowLevelTotalImagesKnown();
protected abstract int LowLevelTotalImages();
A protected abstract ImageSourceNode LowLevelAcquire(int index);
It is important to note that a class that inherits from In addition, The real power in In this class, we want to be able to load every frame of an AVI file. Since AVI files can be read at any point, this is a good candidate for a This class contains a number of PInvoke definitions that link directly to the Win32 AVI calls. A discussion of the operation of these methods is beyond the scope of this document. Most of the work is in opening the AVI file and loading a frame. All the rest of the abstract members of using System;
using System.Runtime.InteropServices;
using Atalasoft.Imaging;
namespace AviSource
{
public class AviImageSource : RandomAccessImageSource
{
string _fileName;
IntPtr _aviFileHandle = IntPtr.Zero;
int _currentFrame = 0;
int _firstFramePosition;
int _totalFrames = 0;
IntPtr _aviStream = IntPtr.Zero;
AVISTREAMINFO _streamInfo = new AVISTREAMINFO();
static AviImageSource()
{
AVIFileInit();
}
public AviImageSource(string fileName)
{
_fileName = fileName;
// LowLevelReset will force the file to be loaded
// and will fetch all the relevant information
LowLevelReset();
}
protected override void LowLevelReset()
{
// attempt to load the file if we haven't
if (_aviFileHandle == IntPtr.Zero)
{
OpenAvi();
LoadAviInfo();
}
// reset the frame counter
_currentFrame = 0;
}
private void CloseAvi()
{
// clear everything out
_currentFrame = 0;
_totalFrames = 0;
// if the file handle is non-null, there may be a stream to close
if (_aviFileHandle != IntPtr.Zero)
{
// if the stream handle is non-null, close it
if (_aviStream != IntPtr.Zero)
{
AVIStreamRelease(_aviStream);
_aviStream = IntPtr.Zero;
}
AVIFileRelease(_aviFileHandle);
_aviFileHandle = IntPtr.Zero;
}
}
private void OpenAvi()
{
// open the file and get a stream interface
int result = AVIFileOpen(out _aviFileHandle, _fileName,
32 /*OF_SHARE_DENY_WRITE*/, 0);
if (result != 0)
throw new Exception("Unable to open avi file " +
_fileName + " (" + result + ")");
result = AVIFileGetStream(_aviFileHandle, out _aviStream,
0x73646976 /* 'vids' -> four char code */, 0);
if (result != 0)
throw new Exception("Unable to get video stream (" +
result + ")");
}
private void LoadAviInfo()
{
if (_aviStream == IntPtr.Zero)
throw new Exception("LoadAviInfo(): Bad stream handle.");
// get first frame
_firstFramePosition = AVIStreamStart(_aviStream);
if (_firstFramePosition < 0)
throw new Exception("LoadAviInfo():" +
" Unable to get stream start position.");
// get total frame count
_totalFrames = AVIStreamLength(_aviStream);
if (_totalFrames < 0)
throw new Exception("LoadAviInfo(): " +
"Unable to get stream length.");
// pull in general information
int result = AVIStreamInfo(_aviStream, ref _streamInfo,
Marshal.SizeOf(_streamInfo));
if (result != 0)
throw new Exception("LoadAviInfo(): unable " +
"to get stream info (" + result + ")");
}
// this method retrieves a frame from the file.
// the class is internal because it will be used by
// the AviImageReloader class.
internal AtalaImage GetAviFrame(int frame)
{
// set up a bitmap info header to make a frame request
BITMAPINFOHEADER bih = new BITMAPINFOHEADER();
bih.biBitCount = 24;
bih.biCompression = 0; //BI_RGB;
bih.biHeight = _streamInfo.frameBottom;
bih.biWidth = _streamInfo.frameRight;
bih.biPlanes = 1;
bih.biSize = (uint)Marshal.SizeOf(bih);
// the getFrameObject is an accessor for retrieving a frame
// from an AVI file. We could make exactly one when the stream
// is opened, but this works just fine.
IntPtr frameAccessor = AVIStreamGetFrameOpen(_aviStream, ref bih);
if (frameAccessor == IntPtr.Zero)
throw new Exception("Unable to get frame decompressor.");
IntPtr theFrame = AVIStreamGetFrame(frameAccessor,
frame + _firstFramePosition);
if (theFrame == IntPtr.Zero)
{
AVIStreamGetFrameClose(frameAccessor);
throw new Exception("Unable to get frame #" + frame);
}
// make a copy of this image
AtalaImage image = AtalaImage.FromDib(theFrame, true);
// closing the frame accessor drops
// the memory used by the frame as well
AVIStreamGetFrameClose(frameAccessor);
return image;
}
protected override ImageSourceNode LowLevelAcquireNextImage()
{
if (_currentFrame >= _totalFrames)
return null;
AtalaImage image = GetAviFrame(_currentFrame);
if (image != null)
{
ImageSourceNode node = new ImageSourceNode(image, null);
_currentFrame++;
return node;
}
return null;
}
protected override ImageSourceNode LowLevelAcquire(int index)
{
if (index < 0 || index >= _totalFrames)
return null;
AtalaImage image = GetAviFrame(index);
if (image != null)
{
ImageSourceNode node = new ImageSourceNode(image,
new AviImageReloader(this, index));
_currentFrame++;
return node;
}
return null;
}
protected override bool LowLevelTotalImagesKnown()
{
return true;
}
protected override int LowLevelTotalImages()
{
return _totalFrames;
}
protected override bool LowLevelHasMoreImages()
{
return _currentFrame < _totalFrames;
}
protected override void LowLevelSkipNextImage()
{
_currentFrame++;
}
protected override bool LowLevelFlushOnReset()
{
return true;
}
protected override void LowLevelDispose()
{
CloseAvi();
}
#region AviHooks
[DllImport("avifil32.dll")]
private static extern void AVIFileInit();
[DllImport("avifil32.dll", PreserveSig=true)]
private static extern int AVIFileOpen(
out IntPtr ppfile,
String szFile,
int uMode,
int pclsidHandler);
[DllImport("avifil32.dll")]
private static extern int AVIFileGetStream(
IntPtr pfile,
out IntPtr ppavi,
int fccType,
int lParam);
[DllImport("avifil32.dll")]
private static extern int AVIStreamRelease(IntPtr aviStream);
[DllImport("avifil32.dll")]
private static extern int AVIFileRelease(IntPtr pfile);
[DllImport("avifil32.dll")]
private static extern void AVIFileExit();
[DllImport("avifil32.dll", PreserveSig=true)]
private static extern int AVIStreamStart(IntPtr pAVIStream);
[DllImport("avifil32.dll", PreserveSig=true)]
private static extern int AVIStreamLength(IntPtr pAVIStream);
[DllImport("avifil32.dll")]
private static extern int AVIStreamInfo(
IntPtr pAVIStream,
ref AVISTREAMINFO psi,
int lSize);
[DllImport("avifil32.dll")]
private static extern IntPtr AVIStreamGetFrameOpen(
IntPtr pAVIStream,
ref BITMAPINFOHEADER bih);
[DllImport("avifil32.dll")]
private static extern IntPtr AVIStreamGetFrame(
IntPtr pGetFrameObj,
int lPos);
[DllImport("avifil32.dll")]
private static extern int AVIStreamGetFrameClose(IntPtr pGetFrameObj);
#endregion
}
}
In addition to this class, it is necessary to have a class that implements using System;
using Atalasoft.Imaging;
namespace AviSource
{
public class AviImageReloader : IImageReloader
{
private int _frame;
private AviImageSource _source;
public AviImageReloader(AviImageSource source, int frame)
{
_source = source;
_frame = frame;
}
#region IImageReloader Members
public AtalaImage Reload()
{
return _source.GetAviFrame(_frame);
}
#endregion
#region IDisposable Members
public void Dispose()
{
}
#endregion
}
}
|
||||||||||||||||||||||||||||||