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 } }