using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using GeoSigma.SigmaDrawerUtil;
#if !MONO_TOUCH
using System.Drawing.Imaging;
#endif
namespace Unplugged.Segy
{
///
/// Responsible for converting SEGY data to a bitmap image
///
public class ImageWriter
{
///
/// When true, sample values that are exactly 0.0f will have their alpha component set to 0.
/// Defaults to true.
///
public bool SetNullValuesToTransparent { get; set; }
public ImageWriter()
{
SetNullValuesToTransparent = false;
}
#if !MONO_TOUCH
public void WriteSliceTime(ISegyFile segyFile, string path
, ValueRange range, int inLineMin, int xlineMin
, int inlineMax, int xlineMax, int time)
{
if (segyFile.Traces == null)
{
using (var bitmap = new Bitmap(1, 1))
bitmap.Save(path);
return;
}
using (var bitmap = GetTimeBitmap(segyFile.Traces, range, inLineMin, xlineMin, inlineMax,xlineMax, time))
bitmap.Save(path, ImageFormat.Png);
}
public void WriteSliceTime(ISegyFile segyFile, string path
, GradientColors colors, int inLineMin, int xlineMin
, int inlineMax, int xlineMax, int time)
{
if (segyFile.Traces == null)
{
using (var bitmap = new Bitmap(1, 1))
bitmap.Save(path);
return;
}
using (var bitmap = GetTimeBitmap(segyFile.Traces, colors, inLineMin, xlineMin, inlineMax, xlineMax, time))
bitmap.Save(path, ImageFormat.Png);
}
public Bitmap GetTimeBitmap(IEnumerable traces
, GradientColors colors, int inLineMin, int xlineMin
, int inlineMax, int xlineMax, int time)
{
dynamic raw = GetTimeRawBytesAndSize(traces, colors, inLineMin, xlineMin,
inlineMax, xlineMax, 4, time);
Bitmap bmpOrigin = GetBitmap(raw.Bytes, raw.Width, raw.Height);
ITrace traceFirst = traces.First();
float dStartX = traceFirst.Header.X;
float dStartY = traceFirst.Header.Y;
float dEndX = 0;
float dEndY = 0;
int nInlineStart = traceFirst.Header.InlineNumber;
int nXlineStart = traceFirst.Header.CrosslineNumber;
// 发现终止点
foreach (ITrace trace in traces)
{
Console.WriteLine($"{trace.Header.InlineNumber}, {trace.Header.CrosslineNumber}");
if (nInlineStart != trace.Header.InlineNumber
&& nXlineStart != trace.Header.CrosslineNumber)
{
break;
}
dEndX = trace.Header.X;
dEndY = trace.Header.Y;
}
float dAngle = (float)Math.Atan2(dEndY - dStartY, dEndX - dStartX) / 0.0174532925f;
return BitmapRotation.Rotate(bmpOrigin, dAngle);
}
public Bitmap GetTimeBitmap(IEnumerable traces
, ValueRange range, int inLineMin, int xlineMin
, int inlineMax, int xlineMax, int time)
{
dynamic raw = GetTimeRawBytesAndSize(traces, range, inLineMin, xlineMin,
inlineMax,xlineMax, 4, time);
Bitmap bmpOrigin = GetBitmap(raw.Bytes, raw.Width, raw.Height);
ITrace traceFirst = traces.First();
float dStartX = traceFirst.Header.X;
float dStartY = traceFirst.Header.Y;
float dEndX = 0;
float dEndY = 0;
int nInlineStart = traceFirst.Header.InlineNumber;
int nXlineStart = traceFirst.Header.CrosslineNumber;
// 发现终止点
foreach (ITrace trace in traces)
{
Console.WriteLine($"{trace.Header.InlineNumber}, {trace.Header.CrosslineNumber}");
if (nInlineStart != trace.Header.InlineNumber
&& nXlineStart != trace.Header.CrosslineNumber)
{
break;
}
dEndX = trace.Header.X;
dEndY = trace.Header.Y;
}
float dAngle = (float)Math.Atan2(dEndY - dStartY, dEndX - dStartX) / 0.0174532925f;
return BitmapRotation.Rotate(bmpOrigin, dAngle);
}
private RawBitmap GetTimeRawBytesAndSize(IEnumerable traces
, ValueRange range, int inLineMin, int xlineMin
, int inlineMax, int xlineMax, int components, int time)
{
var traceList = traces.ToList();
int width = inlineMax - inLineMin + 1;
int height = xlineMax - xlineMin + 1;
if (width == 0)
return new RawBitmap { Bytes = new byte[] { }, Width = 0, Height = 0 };
// ValueRange range = FindRange(traces);
var length = width * height * components;
var bytes = new byte[length];
int i = 0;
int j = 0;
foreach(ITrace trace in traces)
{
i = trace.Header.InlineNumber - inLineMin;
j = trace.Header.CrosslineNumber - xlineMin;
var index = components * (j * width + i);
SetColor(bytes, index, range.Min, range.Delta, trace.Values[time], components);
}
return new RawBitmap { Bytes = bytes, Width = width, Height = height };
}
private RawBitmap GetTimeRawBytesAndSize(IEnumerable traces
, GradientColors colors, int inLineMin, int xlineMin
, int inlineMax, int xlineMax, int components, int time)
{
var traceList = traces.ToList();
int width = inlineMax - inLineMin + 1;
int height = xlineMax - xlineMin + 1;
if (width == 0)
return new RawBitmap { Bytes = new byte[] { }, Width = 0, Height = 0 };
// ValueRange range = FindRange(traces);
var length = width * height * components;
var bytes = new byte[length];
int i = 0;
int j = 0;
foreach (ITrace trace in traces)
{
i = trace.Header.InlineNumber - inLineMin;
j = trace.Header.CrosslineNumber - xlineMin;
var index = components * (j * width + i);
SetColor(bytes, index, colors, trace.Values[time], components);
}
return new RawBitmap { Bytes = bytes, Width = width, Height = height };
}
///
/// Writes one or more bitmap for the given SEGY file.
/// If the SEGY has multiple inline numbers, it is assumed to be 3D, and one bitmap is written for each inline.
///
///
/// The destination path for the image.
/// If multiple images are written, each inline number is added parenthetically to the file name.
///
public virtual void Write(ISegyFile segyFile, string path)
{
if (segyFile.Traces == null)
{
using (var bitmap = new Bitmap(1, 1))
bitmap.Save(path);
return;
}
var inlineNumbers = GetInlineNumbers(segyFile);
if (inlineNumbers.Count() == 1)
Write(segyFile.Traces, path);
else
{
dynamic range = FindRange(segyFile.Traces);
WriteBitmapPerInline(segyFile, path, inlineNumbers, range);
}
}
///
/// Writes a bitmap image with the given traces to the given destination path.
///
public virtual void Write(IEnumerable traces, string path)
{
using (var bitmap = GetBitmap(traces))
bitmap.Save(path);
}
///
/// Returns a single bitmap image composed from all the trace in the SEGY file.
/// The caller is responsible for disposing of the bitmap. This is highy recommended
/// so that GDI resources will be cleaned up.
///
/// A disposable bitmap
public virtual Bitmap GetBitmap(ISegyFile segyFile)
{
return GetBitmap(segyFile.Traces);
}
///
/// Returns a single bitmap image composed from the given traces.
/// The caller is responsible for disposing of the bitmap. This is highy recommended
/// so that GDI resources will be cleaned up.
///
/// A disposable bitmap
public virtual Bitmap GetBitmap(IEnumerable traces)
{
dynamic range = FindRange(traces);
return GetBitmap(traces, range);
}
#endif
///
/// Returns a bitmap as a one-dimensional byte array. Pixels are layed out as R, G, B, A with one byte per channel.
///
public virtual byte[] GetRaw32BppRgba(IEnumerable traces)
{
return GetRaw(traces, 4);
}
///
/// Returns a bitmap as a one-dimensional byte array. The pixel format is 1 unsigned byte per pixel.
/// Thus, the seismic data is quantized to the range 0...255.
///
public virtual byte[] GetRaw8Bpp(IEnumerable traces)
{
return GetRaw(traces, 1);
}
#region Behind the Scenes
private class RawBitmap
{
public int Width { get; set; }
public int Height { get; set; }
public byte[] Bytes { get; set; }
}
// O(N)
private static ValueRange FindRange(IEnumerable traces)
{
var min = float.MaxValue;
var max = float.MinValue;
foreach (var trace in traces)
foreach (var value in trace.Values)
{
if (value < min) min = value;
if (value > max) max = value;
}
return new ValueRange { Min = min, Max = max, Delta = max - min };
}
private byte[] GetRaw(IEnumerable traces, int components)
{
var range = FindRange(traces);
return GetRawBytesAndSize(traces, range, components).Bytes;
}
// O(N)
private RawBitmap GetRawBytesAndSize(IEnumerable traces, ValueRange range, int components)
{
var traceList = traces.ToList();
int width = traceList.Count;
if (width == 0)
return new RawBitmap { Bytes = new byte[] { }, Width = 0, Height = 0 };
int height = traceList.First().Values.Count;
var length = components * width * height;
var bytes = new byte[length];
for (int i = 0; i < width; i++)
for (int j = 0; j < height; j++)
{
var index = components * (j * width + i);
SetColor(bytes, index, range.Min, range.Delta, traceList[i].Values[j], components);
}
return new RawBitmap { Bytes = bytes, Width = width, Height = height };
}
#if !MONO_TOUCH
private void WriteBitmapPerInline(ISegyFile segyFile, string path, IEnumerable inlineNumbers, ValueRange range)
{
foreach (var inline in inlineNumbers)
{
var traces = segyFile.Traces.Where(t => t.Header.InlineNumber == inline);
var filename = Path.GetFileNameWithoutExtension(path);
var extenstion = Path.GetExtension(path);
var newPath = filename + " (" + inline + ")" + extenstion;
WriteBitmapForTraces(traces, newPath, range);
}
}
private void WriteBitmapForTraces(IEnumerable traces, string path, ValueRange range)
{
using (var bitmap = GetBitmap(traces, range))
bitmap.Save(path);
}
private Bitmap GetBitmap(IEnumerable traces, dynamic range)
{
dynamic raw = GetRawBytesAndSize(traces, range, 4);
return GetBitmap(raw.Bytes, raw.Width, raw.Height);
}
private static Bitmap GetBitmap(byte[] bytes, int width, int height)
{
var bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb);
var bitmapData = bitmap.LockBits(Rectangle.FromLTRB(0, 0, width, height), ImageLockMode.WriteOnly, bitmap.PixelFormat);
Marshal.Copy(bytes, 0, bitmapData.Scan0, bytes.Length);
bitmap.UnlockBits(bitmapData);
return bitmap;
}
#endif
private void SetColor(byte[] bytes, int offset, float valueMin, float valueRange, float value, int components)
{
var alpha = byte.MaxValue;
if (SetNullValuesToTransparent && value == 0.0f) // Exactly zero is assumed to be a null sample
alpha = byte.MinValue;
var byteValue = (byte)(byte.MaxValue * (value - valueMin) / valueRange);
if (components == 1)
bytes[offset + 0] = byteValue;
else if (components == 4)
{
bytes[offset + 0] = byteValue;
bytes[offset + 1] = byteValue;
bytes[offset + 2] = byteValue;
bytes[offset + 3] = alpha;
}
else
throw new ArgumentException("Unsupported option", "components");
}
private void SetColor(byte[] bytes, int offset, GradientColors colors, float value, int components)
{
GradientColors gradient = (GradientColors)colors;
Color color = gradient.GetColor(value);
//var alpha = byte.MaxValue;
//if (SetNullValuesToTransparent && value == 0.0f) // Exactly zero is assumed to be a null sample
// alpha = byte.MinValue;
//var byteValue = (byte)(byte.MaxValue * (value - valueMin) / valueRange);
if (components == 1)
bytes[offset + 0] = color.R;
else if (components == 4)
{
bytes[offset + 0] = color.R;
bytes[offset + 1] = color.G;
bytes[offset + 2] = color.B;
bytes[offset + 3] = color.A;
}
else
throw new ArgumentException("Unsupported option", "components");
}
private static IEnumerable GetInlineNumbers(ISegyFile segyFile)
{
return segyFile.Traces.Select(t =>
{
if (t.Header == null)
return 0;
return t.Header.InlineNumber;
}
).Distinct();
}
#endregion
}
}