Skip to content

Instantly share code, notes, and snippets.

@jandk
Created December 16, 2013 10:06
Show Gist options
  • Save jandk/7984759 to your computer and use it in GitHub Desktop.
Save jandk/7984759 to your computer and use it in GitHub Desktop.
Simple image processing in a generic way (just an idea)
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
namespace Util
{
public struct Position
{
public int X { get; set; }
public int Y { get; set; }
}
public interface IRaster<out T>
{
int SizeX { get; }
int SizeY { get; }
T Get(int x, int y);
IEnumerable<T> Enumerate();
}
public interface IWritableRaster<T> : IRaster<T>
{
void Set(int x, int y, T value);
}
public interface IRasterCollection<out T> : IRaster<IRaster<T>>
{
}
public abstract class AbstractRaster<T> : IRaster<T>
{
protected int _sizeX;
protected int _sizeY;
protected AbstractRaster(int sizeX, int sizeY)
{
_sizeX = sizeX;
_sizeY = sizeY;
}
protected AbstractRaster()
{
}
public int SizeX
{
get { return _sizeX; }
}
public int SizeY
{
get { return _sizeY; }
}
public abstract T Get(int x, int y);
public abstract IEnumerable<T> Enumerate();
protected int Index(int x, int y)
{
return y * _sizeX + x;
}
}
#region Source Rasters
public class RawRaster : AbstractRaster<float>, IWritableRaster<float>
{
protected readonly float[] _raster;
public RawRaster(int sizeX, int sizeY)
{
if (sizeX < 0) throw new ArgumentOutOfRangeException("sizeX");
if (sizeY < 0) throw new ArgumentOutOfRangeException("sizeY");
_sizeX = sizeX;
_sizeY = sizeY;
_raster = new float[sizeX * sizeY];
}
public RawRaster(int sizeX, int sizeY, float[] raster)
: this(sizeX, sizeY)
{
if (_raster.Length != raster.Length)
throw new ArgumentException("raster is wrong size");
Array.Copy(raster, _raster, raster.Length);
}
public RawRaster(string path)
{
if (string.IsNullOrWhiteSpace(path))
throw new ArgumentNullException("path");
using (FileStream stream = File.OpenRead(path))
using (var reader = new BinaryReader(stream))
{
_sizeY = reader.ReadInt32BE();
_sizeX = reader.ReadInt32BE();
_raster = new float[_sizeX * _sizeY];
Read(stream);
}
}
public override float Get(int x, int y)
{
Debug.Assert(x >= 0 && x < SizeX);
Debug.Assert(y >= 0 && y < SizeY);
return _raster[Index(x, y)];
}
public void Set(int x, int y, float value)
{
Debug.Assert(x >= 0 && x < SizeX);
Debug.Assert(y >= 0 && y < SizeY);
_raster[Index(x, y)] = value;
}
public override IEnumerable<float> Enumerate()
{
return _raster;
}
private void Read(Stream stream)
{
int numPixels = _sizeX * _sizeY;
int numBytes = numPixels * 2;
// Read raw data
var rawRaster = new byte[numBytes];
int bytesRead = stream.Read(rawRaster, 0, numBytes);
if (bytesRead != numBytes)
throw new IOException("Insufficient data");
// Convert to shorts
Memory.Swap2(rawRaster);
var shortRaster = new short[numPixels];
Buffer.BlockCopy(rawRaster, 0, shortRaster, 0, numBytes);
// Convert to floats
for (int i = 0; i < numPixels; i++)
_raster[i] = shortRaster[i];
}
public void Save(string path)
{
if (string.IsNullOrWhiteSpace(path))
throw new ArgumentNullException("path");
using (FileStream stream = File.OpenWrite(path))
using (var writer = new BinaryWriter(stream))
{
writer.WriteBE(_sizeY);
writer.WriteBE(_sizeX);
// Convert to short
int numPixels = _sizeX * _sizeY;
var shortRaster = new short[numPixels];
for (int i = 0; i < numPixels; i++)
shortRaster[i] = (short)_raster[i];
// Convert to bytes
int numBytes = numPixels * 2;
var rawRaster = new byte[numBytes];
Buffer.BlockCopy(shortRaster, 0, rawRaster, 0, numBytes);
Memory.Swap2(rawRaster);
// Write raw data
stream.Write(rawRaster, 0, numBytes);
}
}
}
#endregion
#region Filter Rasters
public class CropFilter<T> : AbstractRaster<T>
{
protected readonly int _offsetX;
protected readonly int _offsetY;
protected readonly IRaster<T> _source;
public CropFilter(IRaster<T> source, int offsetX, int offsetY, int sizeX, int sizeY)
: base(sizeX, sizeY)
{
_source = source;
_offsetX = offsetX;
_offsetY = offsetY;
}
public override T Get(int x, int y)
{
return _source.Get(_offsetX + x, _offsetY + y);
}
public override IEnumerable<T> Enumerate()
{
int limitX = _offsetX + _sizeX;
int limitY = _offsetY + _sizeY;
for (int y = _offsetY; y < limitY; y++)
for (int x = _offsetX; x < limitX; x++)
yield return _source.Get(x, y);
}
}
public class SubsampleFilter<T> : AbstractRaster<T>
{
protected readonly IRaster<T> _source;
protected readonly int _offsetX;
protected readonly int _offsetY;
protected readonly int _stepX;
protected readonly int _stepY;
public SubsampleFilter(IRaster<T> source, int stepX, int stepY, int offsetX = 0, int offsetY = 0)
{
if (source.SizeX % stepX != 0)
throw new ArgumentException("stepX not a multiple of source.SizeX");
if (source.SizeY % stepY != 0)
throw new ArgumentException("stepY not a multiple of source.SizeY");
if (offsetX < 0 || offsetX >= stepX)
throw new ArgumentOutOfRangeException("offsetX");
if (offsetY < 0 || offsetY >= stepY)
throw new ArgumentOutOfRangeException("offsetY");
_source = source;
_stepX = stepX;
_stepY = stepY;
_offsetX = offsetX;
_offsetY = offsetY;
_sizeX = source.SizeX / stepX;
_sizeY = source.SizeY / stepY;
}
public override T Get(int x, int y)
{
return _source.Get(_stepX * x + _offsetX, _stepY * y + _offsetY);
}
public override IEnumerable<T> Enumerate()
{
int limitX = _source.SizeX;
int limitY = _source.SizeY;
for (int y = _offsetY; y < limitY; y += _stepY)
for (int x = _offsetX; x < limitX; x += _stepX)
yield return _source.Get(x, y);
}
}
public class TileFilter<T> : IRasterCollection<T>
{
protected readonly IRaster<T> _source;
protected readonly int _tileSizeX;
protected readonly int _tileSizeY;
protected readonly IRaster<T>[] _tiles;
public TileFilter(IRaster<T> source, int tileSizeX, int tileSizeY)
{
_source = source;
if (source.SizeX % tileSizeX != 0)
throw new ArgumentException("Cannot divide tiles evenly");
if (source.SizeY % tileSizeY != 0)
throw new ArgumentException("Cannot divide tiles evenly");
_tileSizeX = tileSizeX;
_tileSizeY = tileSizeY;
_tiles = new IRaster<T>[SizeX * SizeY];
}
public int SizeX
{
get { return _source.SizeX / _tileSizeX; }
}
public int SizeY
{
get { return _source.SizeY / _tileSizeY; }
}
public IRaster<T> Get(int x, int y)
{
int index = y * SizeX + x;
if (_tiles[index] == null)
_tiles[index] = new CropFilter<T>(_source, x * _tileSizeX, y * _tileSizeY, _tileSizeX, _tileSizeY);
return _tiles[index];
}
public IEnumerable<IRaster<T>> Enumerate()
{
return _tiles;
}
}
public class Concrete<T> : IRaster<T>
{
protected readonly T[] _raster;
protected readonly IRaster<T> _source;
public Concrete(IRaster<T> source)
{
_source = source;
IEnumerable<T> enumerable = source.Enumerate();
_raster = enumerable as T[] ?? enumerable.ToArray();
}
public int SizeX
{
get { return _source.SizeX; }
}
public int SizeY
{
get { return _source.SizeY; }
}
public T Get(int x, int y)
{
Debug.Assert(x >= 0 && x < SizeX, "x out of range");
Debug.Assert(y >= 0 && y < SizeY, "y out of range");
return _raster[y * SizeX + x];
}
public IEnumerable<T> Enumerate()
{
return _raster;
}
}
public class ColumnFilter<T> : TileFilter<T>
{
public ColumnFilter(IRaster<T> source)
: base(source, 1, source.SizeY)
{
}
}
public class RowFilter<T> : TileFilter<T>
{
public RowFilter(IRaster<T> source)
: base(source, source.SizeX, 1)
{
}
}
#endregion
#region Algorithm
public interface IAlgorithm
{
double Calculate(IEnumerable<float> input);
}
public class MeanAlgorithm : IAlgorithm
{
public double Calculate(IEnumerable<float> input)
{
return input.Average();
}
}
public class MedianAlgorithm : IAlgorithm
{
public double Calculate(IEnumerable<float> input)
{
float[] inputArray = input as float[] ?? input.ToArray();
if (!inputArray.Any())
throw new ArgumentException("input is empty");
Array.Sort(inputArray);
int len = inputArray.Length;
if (len % 2 == 0)
return (inputArray[len / 2 - 1] + inputArray[len / 2]) * .5;
return inputArray[len / 2];
}
}
public class AlgorithmRunner : IRaster<double>
{
private readonly IAlgorithm _algorithm;
private readonly IRasterCollection<float> _source;
public AlgorithmRunner(IRasterCollection<float> source, IAlgorithm algorithm)
{
_source = source;
_algorithm = algorithm;
}
public int SizeX
{
get { return _source.SizeX; }
}
public int SizeY
{
get { return _source.SizeY; }
}
public double Get(int x, int y)
{
Debug.Assert(x >= 0 && x < SizeX, "x out of range");
Debug.Assert(y >= 0 && y < SizeY, "y out of range");
return _algorithm.Calculate(_source.Get(x, y).Enumerate());
}
public IEnumerable<double> Enumerate()
{
int sizeX = SizeX;
int sizeY = SizeY;
for (int y = 0; y < sizeY; y++)
for (int x = 0; x < sizeX; x++)
yield return Get(x, y);
}
}
#endregion
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment