using System; using System.Collections.Generic; using System.IO; using System.Text; namespace Tools { internal enum OperationMode { Open, Create } public class VinnoStreamReader { private readonly Stream _stream; public VinnoStreamReader(Stream stream) { _stream = stream; } /// /// Read string from stream. /// /// public string ReadString() { var dataLength = ReadInt(); var data = new byte[dataLength]; _stream.Read(data, 0, dataLength); return Encoding.Unicode.GetString(data, 0, data.Length); } /// /// Read a int32 value from the stream. /// /// public int ReadInt() { var data = new byte[sizeof(int)]; _stream.Read(data, 0, sizeof(int)); return BitConverter.ToInt32(data, 0); } /// /// Read a short value from the stream. /// /// public short ReadShort() { var data = new byte[sizeof(short)]; _stream.Read(data, 0, sizeof(short)); return BitConverter.ToInt16(data, 0); } /// /// Read a Int64 value from the stream. /// /// public long ReadLong() { var data = new byte[sizeof(long)]; _stream.Read(data, 0, sizeof(long)); return BitConverter.ToInt64(data, 0); } /// /// Read a float value from the stream. /// /// public float ReadFloat() { var data = new byte[sizeof(float)]; _stream.Read(data, 0, sizeof(float)); return BitConverter.ToSingle(data, 0); } /// /// Read a double value from the stream. /// /// public double ReadDouble() { var data = new byte[sizeof(double)]; _stream.Read(data, 0, sizeof(double)); return BitConverter.ToDouble(data, 0); } /// /// Read a bool value from the stream. /// /// public bool ReadBool() { var data = new byte[sizeof(bool)]; _stream.Read(data, 0, sizeof(bool)); return BitConverter.ToBoolean(data, 0); } /// /// Read a byte value from the stream. /// /// public byte ReadByte() { return (byte)(_stream.ReadByte()); } /// /// Read a byte array from the stream. /// /// public byte[] ReadBytes() { var size = ReadInt(); var data = new byte[size]; _stream.Read(data, 0, size); return data; } public long[] ReadLongs() { var data = ReadBytes(); var result = new long[data.Length / sizeof(long)]; Buffer.BlockCopy(data, 0, result, 0, data.Length); return result; } } public class VinnoStreamWriter { private readonly Stream _stream; public VinnoStreamWriter(Stream stream) { _stream = stream; } /// /// Write a string to stream. /// /// public void WriteString(string value) { var data = Encoding.Unicode.GetBytes(value); WriteInt(data.Length); _stream.Write(data, 0, data.Length); } /// /// Write a int32 value to stream. /// /// public void WriteInt(int value) { _stream.Write(BitConverter.GetBytes(value), 0, sizeof(int)); } /// /// Write a short value to stream. /// /// public void WriteShort(short value) { _stream.Write(BitConverter.GetBytes(value), 0, sizeof(short)); } /// /// Write a int64 value to stream. /// /// public void WriteLong(long value) { _stream.Write(BitConverter.GetBytes(value), 0, sizeof(long)); } /// /// Write a float value to stream. /// /// public void WriteFloat(float value) { _stream.Write(BitConverter.GetBytes(value), 0, sizeof(float)); } /// /// Write a double value to stream. /// /// public void WriteDouble(double value) { _stream.Write(BitConverter.GetBytes(value), 0, sizeof(double)); } /// /// Write a bool value to stream. /// /// public void WriteBool(bool value) { _stream.Write(BitConverter.GetBytes(value), 0, sizeof(bool)); } /// /// Write a byte value to stream. /// /// public void WriteByte(byte value) { _stream.WriteByte(value); } /// /// Write a array in to stream. /// public void WriteBytes(byte[] value) { WriteInt(value.Length); _stream.Write(value, 0, value.Length); } /// /// Write long array into stream. /// /// public void WriteLongs(long[] value) { var data = new byte[value.Length * sizeof(long)]; Buffer.BlockCopy(value, 0, data, 0, data.Length); WriteBytes(data); } } public enum VinnoProbeType { Undefined, Linear, Convex, Sector, }; public class VinnoApplication { /// /// Gets the application's ID /// public string ApplicationId { get; } /// /// Gets the OriginalId of the applciation. /// public string ApplicationOriginalId { get; } /// /// Gets the application's name. /// public string ApplicationName { get; } /// /// Gets the applciation's category name. /// public string ApplicationCategoryName { get; } /// /// Gets if is user defined. /// public bool IsUserDefined { get; } public VinnoApplication(string applicationId, string applicationOriginalId, string applicationName, string applicationCategoryName, bool isUserDefined) { ApplicationId = applicationId; ApplicationOriginalId = applicationOriginalId; ApplicationName = applicationName; ApplicationCategoryName = applicationCategoryName; IsUserDefined = isUserDefined; } public byte[] ToBytes() { byte[] result; using (var stream = new MemoryStream()) { var writer = new VinnoStreamWriter(stream); writer.WriteString(ApplicationId); writer.WriteString(ApplicationOriginalId); writer.WriteString(ApplicationName); writer.WriteString(ApplicationCategoryName); writer.WriteBool(IsUserDefined); result = stream.ToArray(); } return result; } public static VinnoApplication FromBytes(byte[] bytes) { VinnoApplication result; using (var stream = new MemoryStream(bytes)) { stream.Position = 0; var reader = new VinnoStreamReader(stream); var applicationId = reader.ReadString(); var applicationOriginalId = reader.ReadString(); var applicationName = reader.ReadString(); var applicationCategoryName = reader.ReadString(); var isUserDefined = reader.ReadBool(); result = new VinnoApplication(applicationId, applicationOriginalId, applicationName, applicationCategoryName, isUserDefined); } return result; } } public class VinnoProbe { public string Name { get; } public VinnoProbeType Type { get; } public VinnoApplication Application { get; } public double FrameRate { get; } public VinnoProbe(string name, VinnoProbeType type, VinnoApplication application, double frameRate) { Name = name; //探头的型号名 Type = type; //探头的类型 Application = application; FrameRate = frameRate; } public byte[] ToBytes() { byte[] result; using (var stream = new MemoryStream()) { var writer = new VinnoStreamWriter(stream); writer.WriteString(Name); writer.WriteByte((byte)Type); writer.WriteBytes(Application.ToBytes()); writer.WriteDouble(FrameRate); result = stream.ToArray(); } return result; } public static VinnoProbe FromBytes(byte[] bytes) { if (bytes == null) { return null; } VinnoProbe result; using (var stream = new MemoryStream(bytes)) { stream.Position = 0; var reader = new VinnoStreamReader(stream); var name = reader.ReadString(); var type = (VinnoProbeType)reader.ReadByte(); var application = VinnoApplication.FromBytes(reader.ReadBytes()); var frameRate = reader.ReadDouble(); if (frameRate == 0) { frameRate = 15; } if (frameRate < 10 && frameRate >= 1) { frameRate = 10; } result = new VinnoProbe(name, type, application, frameRate); } return result; } } internal enum VidImageFormat { Jpeg = 0, Png = 1, H264 = 2, Zip = 3, Diff = 4, } internal interface IImageDataContainer { byte[] ImageData { get; set; } } internal class VinnoImageUpdater { private readonly IImageDataContainer _imageDataContainer; public VinnoImageUpdater(VinnoImage image) { _imageDataContainer = image; } /// /// Update VinnoImage's image data /// /// The image data to be updated. public void Update(byte[] imageData) { _imageDataContainer.ImageData = imageData; } } public enum VinnoVisualType { V2D = 0, V3D, } public enum VinnoVisualIndicator { A = 0, B = 1, C = 2, D = 3 } public enum VinnoDisplayMode { /// /// Normal B mode /// Normal = 0, /// /// Up/Down, B holds 1/3, Other hodes 2/3 /// UpDown13B = 1, /// /// Up/Down, half, half /// UpDownHalfHalf = 2, /// /// Up/Down, B holds2/3, Other hodes1/3 /// UpDown23B = 3, ///// ///// Left/Right, B holds 1/3, other hodes 2/3 ///// //SideBySide13B = 3, /// /// Left/Right, half, half /// SideBySideHalfHalf = 4, /// /// Left/Right, B holds2/3, Other hodes1/3 /// SideBySide14BOther = 5, /// /// All Image area are Other image instead of B image /// FullOther = 6, FullTissue = 7, } public enum VinnoModeType { Undefined = 0, Tissue, Flow, Doppler, TissueTM, Tissue3D, FlowM, }; public class VinnoMode { public string Name { get; } public string DisplayName { get; } public VinnoModeType Type { get; } public VinnoMode(string name, string displayName, VinnoModeType type) { Name = name; DisplayName = displayName; Type = type; } public byte[] ToBytes() { byte[] result; using (var stream = new MemoryStream()) { var writer = new VinnoStreamWriter(stream); writer.WriteString(Name); writer.WriteString(DisplayName); writer.WriteByte((byte)Type); result = stream.ToArray(); } return result; } public static VinnoMode FromBytes(byte[] bytes) { VinnoMode result; using (var stream = new MemoryStream(bytes)) { stream.Position = 0; var reader = new VinnoStreamReader(stream); var name = reader.ReadString(); var displayName = reader.ReadString(); var type = (VinnoModeType)reader.ReadByte(); result = new VinnoMode(name, displayName, type); } return result; } } internal enum VinnoVisualAreaType { Tissue = 0, Flow, TimeMotion, Doppler, TissueTimeMotion, Trace, Colorbar, Tissue3D, ZoomTissue, ZoomFlow, AssociateTissue, AssociateFlow, } internal enum VinnoUnit { None = 0, percent = 1, cm = 10, mm = 11, inch = 12, ft = 13, s = 20, minute = 21, hour = 22, day = 23, week = 24, week_day = 25, Tick = 26, msec = 27, degree = 30, radian = 31, g = 40, mg = 41, ng = 42, kg = 43, oz = 44, lb = 45, cm2 = 50, mm2 = 51, m2 = 52, cm3 = 60, mm3 = 61, ml = 62, L = 63, cms = 70, mms = 71, ms = 72, cms2 = 80, mms2 = 81, cm3s = 90, mls = 91, mlmin = 92, Lmin = 93, gcm3 = 100, gml = 101, ngml = 102, mmHg = 110, mV = 120, Hz = 130, KHz = 131, /// /// beats per minute /// HR = 132, //SI cm3m2 = 140, mlm2 = 141, //CI cm3sm2 = 150, mlsm2 = 151, cm3minm2 = 153, mlminm2 = 154, Lminm2 = 155, /// /// MVCF:mean velocity of circumferential fiber shortening /// circs = 160, //CO mlbeat = 170, mm2pa = 180, d1mpa = 181, kpa = 182, mmHgs = 190, gm2 = 200, /// AVA Index cm2m2 = 210 } internal class VinnoPoint { public double X { get; } public double Y { get; } public VinnoPoint(double x, double y) { X = x; Y = y; } public override string ToString() { return $"X:{X},Y:{Y}"; } protected bool Equals(VinnoPoint other) { return X.Equals(other.X) && Y.Equals(other.Y); } public override bool Equals(object obj) { if (ReferenceEquals(null, obj)) return false; if (ReferenceEquals(this, obj)) return true; if (obj.GetType() != this.GetType()) return false; return Equals((VinnoPoint)obj); } public override int GetHashCode() { unchecked { return (X.GetHashCode() * 397) ^ Y.GetHashCode(); } } public static bool operator ==(VinnoPoint left, VinnoPoint right) { return Equals(left, right); } public static bool operator !=(VinnoPoint left, VinnoPoint right) { return !Equals(left, right); } } internal class VinnoRect { public double Left { get; } public double Right { get; } public double Top { get; } public double Bottom { get; } public double Width => Math.Abs(Right - Left); public double Height => Math.Abs(Bottom - Top); public VinnoPoint TopLeft => new VinnoPoint(Left, Top); public VinnoPoint TopRight => new VinnoPoint(Right, Top); public VinnoPoint BottomLeft => new VinnoPoint(Left, Bottom); public VinnoPoint BottomRight => new VinnoPoint(Right, Bottom); public VinnoRect(double x, double y, double width, double height) { if ((width < 0.0) || (height < 0.0)) { throw new ArgumentException("width and height can not less than 0."); } Left = x; Top = y; Right = Left + width; Bottom = Top + height; } public VinnoRect(VinnoPoint topLeft, VinnoPoint bottomRight) { Left = topLeft.X; Top = topLeft.Y; Right = bottomRight.X; Bottom = bottomRight.Y; } public static bool operator ==(VinnoRect rect1, VinnoRect rect2) { return ((((rect1.Left == rect2.Left) && (rect1.Right == rect2.Right)) && (rect1.Bottom == rect2.Bottom)) && (rect1.Top == rect2.Top)); } public static bool operator !=(VinnoRect rect1, VinnoRect rect2) { return !(rect1 == rect2); } public static bool Equals(VinnoRect rect1, VinnoRect rect2) { return ((((rect1.Left == rect2.Left) && (rect1.Right == rect2.Right)) && (rect1.Bottom == rect2.Bottom)) && (rect1.Top == rect2.Top)); } public override bool Equals(object o) { if ((o == null) || !(o is VinnoRect)) { return false; } VinnoRect rect = (VinnoRect)o; return Equals(this, rect); } public bool Equals(VinnoRect value) { return Equals(this, value); } public override int GetHashCode() { return (((Left.GetHashCode() ^ Right.GetHashCode()) ^ Top.GetHashCode()) ^ Bottom.GetHashCode()); } public override string ToString() { return $"L:{Left},R:{Right},T:{Top},B:{Bottom}"; } } internal class VinnoLogicalCoordinate { public bool IsFlipHorizontal { get; } public bool IsFlipVertical { get; } public VinnoRect Region { get; } public VinnoUnit XUnit { get; } public VinnoUnit YUnit { get; } public VinnoLogicalCoordinate(bool isFlipHorizontal, bool isFlipVertical, VinnoRect region, VinnoUnit xUnit, VinnoUnit yUnit) { YUnit = yUnit; XUnit = xUnit; Region = region; IsFlipVertical = isFlipVertical; IsFlipHorizontal = isFlipHorizontal; } public byte[] ToBytes() { byte[] result; using (var stream = new MemoryStream()) { var writer = new VinnoStreamWriter(stream); writer.WriteBool(IsFlipHorizontal); writer.WriteBool(IsFlipVertical); writer.WriteByte((byte)XUnit); writer.WriteByte((byte)YUnit); writer.WriteDouble(Region.Left); writer.WriteDouble(Region.Top); writer.WriteDouble(Region.Right); writer.WriteDouble(Region.Bottom); result = stream.ToArray(); } return result; } public static VinnoLogicalCoordinate FromBytes(byte[] bytes) { VinnoLogicalCoordinate result; using (var stream = new MemoryStream(bytes)) { stream.Position = 0; var reader = new VinnoStreamReader(stream); var isFlipHorizontal = reader.ReadBool(); var isFlipVertical = reader.ReadBool(); var xUnit = (VinnoUnit)reader.ReadByte(); var yUnit = (VinnoUnit)reader.ReadByte(); var left = reader.ReadDouble(); var top = reader.ReadDouble(); var right = reader.ReadDouble(); var bottom = reader.ReadDouble(); var region = new VinnoRect(new VinnoPoint(left, top), new VinnoPoint(right, bottom)); result = new VinnoLogicalCoordinate(isFlipHorizontal, isFlipVertical, region, xUnit, yUnit); } return result; } } internal enum PhysicalCoordinateType { Tissue = 0, TimeMotion, ConvexTissue, LinearTissue, ConvexTVTissue, LinearTVTissue, Doppler, TissueTimeMotion, MAM, PWV } internal abstract class VinnoPhysicalCoordinate { public PhysicalCoordinateType Type { get; protected set; } public virtual byte[] ToBytes() { byte[] result; using (var stream = new MemoryStream()) { var writer = new VinnoStreamWriter(stream); writer.WriteByte((byte)Type); result = stream.ToArray(); } return result; } public static VinnoPhysicalCoordinate FromBytes(byte[] bytes) { VinnoPhysicalCoordinate physicalCoordinate = null; using (var stream = new MemoryStream(bytes)) { stream.Position = 0; var reader = new VinnoStreamReader(stream); var type = (PhysicalCoordinateType)reader.ReadByte(); switch (type) { case PhysicalCoordinateType.ConvexTissue: physicalCoordinate = VinnoConvexTissuePhysicalCoordinate.FromBytes(bytes); break; case PhysicalCoordinateType.LinearTissue: physicalCoordinate = VinnoLinearTissuePhysicalCoordinate.FromBytes(bytes); break; case PhysicalCoordinateType.ConvexTVTissue: physicalCoordinate = VinnoConvexTVTissuePhysicalCoordinate.FromBytes(bytes); break; case PhysicalCoordinateType.LinearTVTissue: physicalCoordinate = VinnoLinearTVTissuePhysicalCoordinate.FromBytes(bytes); break; case PhysicalCoordinateType.Doppler: physicalCoordinate = VinnoDopplerPhysicalCoordinate.FromBytes(bytes); break; case PhysicalCoordinateType.TissueTimeMotion: physicalCoordinate = VinnoTissueTimeMotionPhysicalCoordinate.FromBytes(bytes); break; case PhysicalCoordinateType.MAM: physicalCoordinate = VinnoMAMPhysicalCoordinate.FromBytes(bytes); break; case PhysicalCoordinateType.PWV: physicalCoordinate = VinnoPWVPhysicalCoordinate.FromBytes(bytes); break; } } return physicalCoordinate; } } internal abstract class VinnoTissuePhysicalCoordinate : VinnoPhysicalCoordinate { public double DepthEnd { get; } public double DepthStart { get; } public double Width { get; } public double BeamPosition { get; } protected VinnoTissuePhysicalCoordinate(double depthEnd, double depthStart, double width, double beamPosition) { Type = PhysicalCoordinateType.Tissue; BeamPosition = beamPosition; Width = width; DepthStart = depthStart; DepthEnd = depthEnd; } public override byte[] ToBytes() { byte[] result; using (var stream = new MemoryStream()) { var baseData = base.ToBytes(); stream.Write(baseData, 0, baseData.Length); stream.Position = stream.Length; var writer = new VinnoStreamWriter(stream); writer.WriteDouble(DepthStart); writer.WriteDouble(DepthEnd); writer.WriteDouble(Width); writer.WriteDouble(BeamPosition); result = stream.ToArray(); } return result; } } internal abstract class VinnoTimeMotionPhysicalCoordinate : VinnoPhysicalCoordinate { public double SweepSpeed { get; } public double Max { get; } public double Min { get; } protected VinnoTimeMotionPhysicalCoordinate(double sweepSpeed, double max, double min) { Type = PhysicalCoordinateType.TimeMotion; Min = min; Max = max; SweepSpeed = sweepSpeed; } public override byte[] ToBytes() { byte[] result; using (var stream = new MemoryStream()) { var baseData = base.ToBytes(); stream.Write(baseData, 0, baseData.Length); stream.Position = stream.Length; var writer = new VinnoStreamWriter(stream); writer.WriteDouble(Min); writer.WriteDouble(Max); writer.WriteDouble(SweepSpeed); result = stream.ToArray(); } return result; } } internal class VinnoConvexTissuePhysicalCoordinate : VinnoTissuePhysicalCoordinate { public double ZeroRadius { get; private set; } public VinnoConvexTissuePhysicalCoordinate(double depthEnd, double depthStart, double width, double beamPosition, double zeroRadius) : base(depthEnd, depthStart, width, beamPosition) { Type = PhysicalCoordinateType.ConvexTissue; ZeroRadius = zeroRadius; } public override byte[] ToBytes() { byte[] result; using (var stream = new MemoryStream()) { var baseData = base.ToBytes(); stream.Write(baseData, 0, baseData.Length); stream.Position = stream.Length; var writer = new VinnoStreamWriter(stream); writer.WriteDouble(ZeroRadius); result = stream.ToArray(); } return result; } public new static VinnoPhysicalCoordinate FromBytes(byte[] bytes) { VinnoPhysicalCoordinate result; using (var stream = new MemoryStream(bytes)) { stream.Position = 0; var reader = new VinnoStreamReader(stream); var type = (PhysicalCoordinateType)reader.ReadByte(); if (type != PhysicalCoordinateType.ConvexTissue) { throw new InvalidCastException($"Type not matched, target type:{PhysicalCoordinateType.ConvexTissue}, source type:{type}"); } var depthStart = reader.ReadDouble(); var depthEnd = reader.ReadDouble(); var width = reader.ReadDouble(); var beamPosition = reader.ReadDouble(); var zeroRadius = reader.ReadDouble(); result = new VinnoConvexTissuePhysicalCoordinate(depthEnd, depthStart, width, beamPosition, zeroRadius); } return result; } } internal class VinnoLinearTissuePhysicalCoordinate : VinnoTissuePhysicalCoordinate { public double Steer { get; } public VinnoLinearTissuePhysicalCoordinate(double depthEnd, double depthStart, double width, double beamPosition, double steer) : base(depthEnd, depthStart, width, beamPosition) { Type = PhysicalCoordinateType.LinearTissue; Steer = steer; } public override byte[] ToBytes() { byte[] result; using (var stream = new MemoryStream()) { var baseData = base.ToBytes(); stream.Write(baseData, 0, baseData.Length); stream.Position = stream.Length; var writer = new VinnoStreamWriter(stream); writer.WriteDouble(Steer); result = stream.ToArray(); } return result; } public new static VinnoPhysicalCoordinate FromBytes(byte[] bytes) { VinnoPhysicalCoordinate result; using (var stream = new MemoryStream(bytes)) { stream.Position = 0; var reader = new VinnoStreamReader(stream); var type = (PhysicalCoordinateType)reader.ReadByte(); if (type != PhysicalCoordinateType.LinearTissue) { throw new InvalidCastException($"Type not matched, target type:{PhysicalCoordinateType.LinearTissue}, source type:{type}"); } var depthStart = reader.ReadDouble(); var depthEnd = reader.ReadDouble(); var width = reader.ReadDouble(); var beamPosition = reader.ReadDouble(); var steer = reader.ReadDouble(); result = new VinnoLinearTissuePhysicalCoordinate(depthEnd, depthStart, width, beamPosition, steer); } return result; } } internal class VinnoConvexTVTissuePhysicalCoordinate : VinnoConvexTissuePhysicalCoordinate { public double OriginalZeroRadius { get; private set; } public double OriginalRocx { get; private set; } public VinnoConvexTVTissuePhysicalCoordinate(double depthEnd, double depthStart, double width, double beamPosition, double zeroRadius, double originalZeroRadius, double originalRocx) : base(depthEnd, depthStart, width, beamPosition, zeroRadius) { Type = PhysicalCoordinateType.ConvexTVTissue; OriginalRocx = originalRocx; OriginalZeroRadius = originalZeroRadius; } public override byte[] ToBytes() { byte[] result; using (var stream = new MemoryStream()) { var baseData = base.ToBytes(); stream.Write(baseData, 0, baseData.Length); stream.Position = stream.Length; var writer = new VinnoStreamWriter(stream); writer.WriteDouble(OriginalZeroRadius); writer.WriteDouble(OriginalRocx); result = stream.ToArray(); } return result; } public new static VinnoPhysicalCoordinate FromBytes(byte[] bytes) { VinnoPhysicalCoordinate result; using (var stream = new MemoryStream(bytes)) { stream.Position = 0; var reader = new VinnoStreamReader(stream); var type = (PhysicalCoordinateType)reader.ReadByte(); if (type != PhysicalCoordinateType.ConvexTVTissue) { throw new InvalidCastException($"Type not matched, target type:{PhysicalCoordinateType.ConvexTVTissue}, source type:{type}"); } var depthStart = reader.ReadDouble(); var depthEnd = reader.ReadDouble(); var width = reader.ReadDouble(); var beamPosition = reader.ReadDouble(); var zeroRadius = reader.ReadDouble(); var originalZeroRadius = reader.ReadDouble(); var originalRocx = reader.ReadDouble(); result = new VinnoConvexTVTissuePhysicalCoordinate(depthEnd, depthStart, width, beamPosition, zeroRadius, originalZeroRadius, originalRocx); } return result; } } internal class VinnoLinearTVTissuePhysicalCoordinate : VinnoConvexTissuePhysicalCoordinate { public VinnoLinearTVTissuePhysicalCoordinate(double depthEnd, double depthStart, double width, double beamPosition, double zeroRadius) : base(depthEnd, depthStart, width, beamPosition, zeroRadius) { Type = PhysicalCoordinateType.LinearTVTissue; } public new static VinnoPhysicalCoordinate FromBytes(byte[] bytes) { VinnoPhysicalCoordinate result; using (var stream = new MemoryStream(bytes)) { stream.Position = 0; var reader = new VinnoStreamReader(stream); var type = (PhysicalCoordinateType)reader.ReadByte(); if (type != PhysicalCoordinateType.LinearTVTissue) { throw new InvalidCastException($"Type not matched, target type:{PhysicalCoordinateType.LinearTVTissue}, source type:{type}"); } var depthStart = reader.ReadDouble(); var depthEnd = reader.ReadDouble(); var width = reader.ReadDouble(); var beamPosition = reader.ReadDouble(); var zeroRadius = reader.ReadDouble(); result = new VinnoLinearTVTissuePhysicalCoordinate(depthEnd, depthStart, width, beamPosition, zeroRadius); } return result; } } internal class VinnoDopplerPhysicalCoordinate : VinnoTimeMotionPhysicalCoordinate { public double BaseLine { get; } public VinnoDopplerPhysicalCoordinate(double sweepSpeed, double max, double min, double baseLine) : base(sweepSpeed, max, min) { Type = PhysicalCoordinateType.Doppler; BaseLine = baseLine; } public override byte[] ToBytes() { byte[] result; using (var stream = new MemoryStream()) { var baseData = base.ToBytes(); stream.Write(baseData, 0, baseData.Length); stream.Position = stream.Length; var writer = new VinnoStreamWriter(stream); writer.WriteDouble(BaseLine); result = stream.ToArray(); } return result; } public new static VinnoPhysicalCoordinate FromBytes(byte[] bytes) { VinnoPhysicalCoordinate result; using (var stream = new MemoryStream(bytes)) { stream.Position = 0; var reader = new VinnoStreamReader(stream); var type = (PhysicalCoordinateType)reader.ReadByte(); if (type != PhysicalCoordinateType.Doppler) { throw new InvalidCastException($"Type not matched, target type:{PhysicalCoordinateType.Doppler}, source type:{type}"); } var min = reader.ReadDouble(); var max = reader.ReadDouble(); var sweepSpeed = reader.ReadDouble(); var baseLine = reader.ReadDouble(); result = new VinnoDopplerPhysicalCoordinate(sweepSpeed, max, min, baseLine); } return result; } } internal class VinnoTissueTimeMotionPhysicalCoordinate : VinnoTimeMotionPhysicalCoordinate { public double DepthStart { get; } public double DepthEnd { get; } public VinnoTissueTimeMotionPhysicalCoordinate(double sweepSpeed, double max, double min, double depthStart, double depthEnd) : base(sweepSpeed, max, min) { Type = PhysicalCoordinateType.TissueTimeMotion; DepthEnd = depthEnd; DepthStart = depthStart; } public override byte[] ToBytes() { byte[] result; using (var stream = new MemoryStream()) { var baseData = base.ToBytes(); stream.Write(baseData, 0, baseData.Length); stream.Position = stream.Length; var writer = new VinnoStreamWriter(stream); writer.WriteDouble(DepthStart); writer.WriteDouble(DepthEnd); result = stream.ToArray(); } return result; } public new static VinnoPhysicalCoordinate FromBytes(byte[] bytes) { VinnoPhysicalCoordinate result; using (var stream = new MemoryStream(bytes)) { stream.Position = 0; var reader = new VinnoStreamReader(stream); var type = (PhysicalCoordinateType)reader.ReadByte(); if (type != PhysicalCoordinateType.TissueTimeMotion) { throw new InvalidCastException($"Type not matched, target type:{PhysicalCoordinateType.TissueTimeMotion}, source type:{type}"); } var min = reader.ReadDouble(); var max = reader.ReadDouble(); var sweepSpeed = reader.ReadDouble(); var depthStart = reader.ReadDouble(); var depthEnd = reader.ReadDouble(); result = new VinnoTissueTimeMotionPhysicalCoordinate(sweepSpeed, max, min, depthStart, depthEnd); } return result; } } internal class VinnoMAMPhysicalCoordinate : VinnoTissueTimeMotionPhysicalCoordinate { public VinnoMAMPhysicalCoordinate(double sweepSpeed, double max, double min, double depthStart, double depthEnd) : base(sweepSpeed, max, min, depthStart, depthEnd) { Type = PhysicalCoordinateType.MAM; } public new static VinnoPhysicalCoordinate FromBytes(byte[] bytes) { VinnoPhysicalCoordinate result; using (var stream = new MemoryStream(bytes)) { stream.Position = 0; var reader = new VinnoStreamReader(stream); var type = (PhysicalCoordinateType)reader.ReadByte(); if (type != PhysicalCoordinateType.MAM) { throw new InvalidCastException($"Type not matched, target type:{PhysicalCoordinateType.MAM}, source type:{type}"); } var min = reader.ReadDouble(); var max = reader.ReadDouble(); var sweepSpeed = reader.ReadDouble(); var depthStart = reader.ReadDouble(); var depthEnd = reader.ReadDouble(); result = new VinnoMAMPhysicalCoordinate(sweepSpeed, max, min, depthStart, depthEnd); } return result; } } internal class VinnoPWVPhysicalCoordinate : VinnoTissueTimeMotionPhysicalCoordinate { public VinnoPWVPhysicalCoordinate(double sweepSpeed, double max, double min, double depthStart, double depthEnd) : base(sweepSpeed, max, min, depthStart, depthEnd) { Type = PhysicalCoordinateType.PWV; } public new static VinnoPhysicalCoordinate FromBytes(byte[] bytes) { VinnoPhysicalCoordinate result; using (var stream = new MemoryStream(bytes)) { stream.Position = 0; var reader = new VinnoStreamReader(stream); var type = (PhysicalCoordinateType)reader.ReadByte(); if (type != PhysicalCoordinateType.PWV) { throw new InvalidCastException($"Type not matched, target type:{PhysicalCoordinateType.PWV}, source type:{type}"); } var min = reader.ReadDouble(); var max = reader.ReadDouble(); var sweepSpeed = reader.ReadDouble(); var depthStart = reader.ReadDouble(); var depthEnd = reader.ReadDouble(); result = new VinnoPWVPhysicalCoordinate(sweepSpeed, max, min, depthStart, depthEnd); } return result; } } internal class Vinno2DVisual : VinnoVisual { /// /// Gets all LogicalCoordinates of this visual. /// public Dictionary LogicalCoordinates { get; } /// /// Gets all PhysicalCoordinates of this visual. /// public Dictionary PhysicalCoordinates { get; } public Vinno2DVisual() { VisualType = VinnoVisualType.V2D; LogicalCoordinates = new Dictionary(); PhysicalCoordinates = new Dictionary(); } public override byte[] ToBytes() { var baseData = base.ToBytes(); byte[] result; using (var stream = new MemoryStream()) { stream.Write(baseData, 0, baseData.Length); var writer = new VinnoStreamWriter(stream); writer.WriteByte((byte)PhysicalCoordinates.Count); foreach (var key in PhysicalCoordinates.Keys) { writer.WriteByte((byte)key); writer.WriteBytes(PhysicalCoordinates[key].ToBytes()); } writer.WriteByte((byte)LogicalCoordinates.Count); foreach (var key in LogicalCoordinates.Keys) { writer.WriteByte((byte)key); writer.WriteBytes(LogicalCoordinates[key].ToBytes()); } result = stream.ToArray(); } return result; } public new static VinnoVisual FromBytes(byte[] bytes) { Vinno2DVisual result = null; using (var stream = new MemoryStream(bytes)) { stream.Position = 0; var reader = new VinnoStreamReader(stream); var visualType = (VinnoVisualType)reader.ReadByte(); var displayMode = (VinnoDisplayMode)reader.ReadByte(); if (visualType == VinnoVisualType.V2D) { var indicator = (VinnoVisualIndicator)reader.ReadByte(); var activeModeType = (VinnoModeType)reader.ReadByte(); var modeCount = reader.ReadByte(); var modes = new List(); for (var i = 0; i < modeCount; i++) { var mode = VinnoMode.FromBytes(reader.ReadBytes()); modes.Add(mode); } result = new Vinno2DVisual { Indicator = indicator }; foreach (var mode in modes) { result.Modes.Add(mode); } result.ActiveModeType = activeModeType; var physicalCoordinateCount = reader.ReadByte(); var physicalCoordinates = new Dictionary(); for (var i = 0; i < physicalCoordinateCount; i++) { var visualAreaType = (VinnoVisualAreaType)reader.ReadByte(); var physicalCoordinate = VinnoPhysicalCoordinate.FromBytes(reader.ReadBytes()); physicalCoordinates.Add(visualAreaType, physicalCoordinate); } var logicalCoordinateCount = reader.ReadByte(); var logicalCoordinates = new Dictionary(); for (var i = 0; i < logicalCoordinateCount; i++) { var visualAreaType = (VinnoVisualAreaType)reader.ReadByte(); var logicalCoordinate = VinnoLogicalCoordinate.FromBytes(reader.ReadBytes()); logicalCoordinates.Add(visualAreaType, logicalCoordinate); } foreach (var key in physicalCoordinates.Keys) { result.PhysicalCoordinates.Add(key, physicalCoordinates[key]); } foreach (var key in logicalCoordinates.Keys) { result.LogicalCoordinates.Add(key, logicalCoordinates[key]); } result.DisplayMode = displayMode; } } return result; } } internal enum VinnoAreaIndicator { Global = 0, A, B, C, ThreeD } internal class VinnoTissue3DArea { public VinnoRect Bounds { get; } public double CmPerPixel { get; } public VinnoPoint GlobalOffset { get; } public VinnoAreaIndicator Indicator { get; } public VinnoTissue3DArea(VinnoRect bounds, double cmPerPixel, VinnoPoint globalOffset, VinnoAreaIndicator indicator) { Indicator = indicator; GlobalOffset = globalOffset; CmPerPixel = cmPerPixel; Bounds = bounds; } public byte[] ToBytes() { byte[] result; using (var stream = new MemoryStream()) { var writer = new VinnoStreamWriter(stream); writer.WriteByte((byte)Indicator); writer.WriteDouble(CmPerPixel); writer.WriteDouble(GlobalOffset.X); writer.WriteDouble(GlobalOffset.Y); writer.WriteDouble(Bounds.Left); writer.WriteDouble(Bounds.Top); writer.WriteDouble(Bounds.Right); writer.WriteDouble(Bounds.Bottom); result = stream.ToArray(); } return result; } public static VinnoTissue3DArea FromBytes(byte[] tissue3DAreaData) { VinnoTissue3DArea result; using (var stream = new MemoryStream(tissue3DAreaData)) { var reader = new VinnoStreamReader(stream); var indicator = (VinnoAreaIndicator)reader.ReadByte(); var cmPerPixel = reader.ReadDouble(); var x = reader.ReadDouble(); var y = reader.ReadDouble(); var globalOffset = new VinnoPoint(x, y); var left = reader.ReadDouble(); var top = reader.ReadDouble(); var right = reader.ReadDouble(); var bottom = reader.ReadDouble(); var bounds = new VinnoRect(new VinnoPoint(left, top), new VinnoPoint(right, bottom)); result = new VinnoTissue3DArea(bounds, cmPerPixel, globalOffset, indicator); } return result; } } internal class Vinno3DVisual : VinnoVisual { /// /// Gets the tissue 3d areas for this visual. /// public IList Tissue3DAreas { get; } public Vinno3DVisual() { VisualType = VinnoVisualType.V3D; Tissue3DAreas = new List(); } public override byte[] ToBytes() { var baseData = base.ToBytes(); byte[] result; using (var stream = new MemoryStream()) { stream.Write(baseData, 0, baseData.Length); var writer = new VinnoStreamWriter(stream); writer.WriteByte((byte)Tissue3DAreas.Count); foreach (var tissue3DArea in Tissue3DAreas) { writer.WriteBytes(tissue3DArea.ToBytes()); } result = stream.ToArray(); } return result; } public new static VinnoVisual FromBytes(byte[] bytes) { Vinno3DVisual result = null; using (var stream = new MemoryStream(bytes)) { stream.Position = 0; var reader = new VinnoStreamReader(stream); var visualType = (VinnoVisualType)reader.ReadByte(); var displayMode = (VinnoDisplayMode)reader.ReadByte(); if (visualType == VinnoVisualType.V3D) { var indicator = (VinnoVisualIndicator)reader.ReadByte(); result = new Vinno3DVisual { Indicator = indicator }; var activeModeType = (VinnoModeType)reader.ReadByte(); var modeCount = reader.ReadByte(); var modes = new List(); for (int i = 0; i < modeCount; i++) { var mode = VinnoMode.FromBytes(reader.ReadBytes()); modes.Add(mode); } foreach (var mode in modes) { result.Modes.Add(mode); } result.ActiveModeType = activeModeType; var tissue3DAreaCount = reader.ReadByte(); var tissue3DAreas = new List(); for (int i = 0; i < tissue3DAreaCount; i++) { var tissue3DArea = VinnoTissue3DArea.FromBytes(reader.ReadBytes()); tissue3DAreas.Add(tissue3DArea); } foreach (var tissue3DArea in tissue3DAreas) { result.Tissue3DAreas.Add(tissue3DArea); } result.DisplayMode = displayMode; } } return result; } } public class VinnoVisual { /// /// Gets or sets the Display mode of this image. /// public VinnoDisplayMode DisplayMode { get; set; } /// /// Gets the visual type of this Visual. /// protected VinnoVisualType VisualType; /// /// Gets or sets the Indicator of this Visual. /// public VinnoVisualIndicator Indicator { get; set; } /// /// Gets or set the active mode type of this visual. /// public VinnoModeType ActiveModeType { get; set; } /// /// Gets all modse of this visual. /// public IList Modes { get; } public VinnoVisual() { ActiveModeType = VinnoModeType.Undefined; Modes = new List(); //Default is A Indicator = VinnoVisualIndicator.A; //Default is Normal DisplayMode = VinnoDisplayMode.Normal; } public virtual byte[] ToBytes() { byte[] result; using (var stream = new MemoryStream()) { var writer = new VinnoStreamWriter(stream); writer.WriteByte((byte)VisualType); writer.WriteByte((byte)DisplayMode); writer.WriteByte((byte)Indicator); writer.WriteByte((byte)ActiveModeType); writer.WriteByte((byte)Modes.Count); foreach (var mode in Modes) { writer.WriteBytes(mode.ToBytes()); } result = stream.ToArray(); } return result; } public static VinnoVisual FromBytes(byte[] bytes) { VinnoVisual result = null; using (var stream = new MemoryStream(bytes)) { stream.Position = 0; var reader = new VinnoStreamReader(stream); var visualType = (VinnoVisualType)reader.ReadByte(); if (visualType == VinnoVisualType.V2D) { result = Vinno2DVisual.FromBytes(bytes); } if (visualType == VinnoVisualType.V3D) { result = Vinno3DVisual.FromBytes(bytes); } } return result; } } internal class VinnoImage : IImageDataContainer { /// /// Gets the index of this image. /// public int Index { get; } /// /// Gets the width of this image data. /// public int Width { get; } /// /// Gets the height of this image data. /// public int Height { get; } /// /// Gets the image data of this image. /// public byte[] ImageData { get; private set; } /// /// Gets all visuals of this image. /// public IList Visuals { get; } /// /// Implement the interface for update the image data. /// byte[] IImageDataContainer.ImageData { get => ImageData; set => ImageData = value; } public VinnoImage(int index, int width, int height, byte[] imageData) { Visuals = new List(); Index = index; Width = width; Height = height; ImageData = imageData; } /// /// Convert image to bytes. /// /// The converted bytes. public byte[] ToBytes() { byte[] result = new byte[0]; using (var stream = new MemoryStream()) { var writer = new VinnoStreamWriter(stream); if (writer != null) { writer.WriteInt(Index); writer.WriteByte((byte)Visuals.Count); foreach (var visual in Visuals) { var buffer = visual?.ToBytes(); if (buffer != null) { writer.WriteBytes(buffer); } } writer.WriteShort((short)Width); writer.WriteShort((short)Height); writer.WriteBytes(ImageData); result = stream.ToArray(); } } return result; } /// /// Convert bytes to a /// /// The bytes to be converted. /// The converted public static VinnoImage FromBytes(byte[] bytes) { VinnoImage result; using (var stream = new MemoryStream(bytes)) { stream.Position = 0; var reader = new VinnoStreamReader(stream); var index = reader.ReadInt(); var visualCount = reader.ReadByte(); var visuals = new List(); for (int i = 0; i < visualCount; i++) { var visual = VinnoVisual.FromBytes(reader.ReadBytes()); visuals.Add(visual); } var widht = reader.ReadShort(); var height = reader.ReadShort(); var imageData = reader.ReadBytes(); result = new VinnoImage(index, widht, height, imageData); foreach (var visual in visuals) { result.Visuals.Add(visual); } } return result; } } internal class VinnoImageData : IDisposable { private readonly OperationMode _operationMode; private readonly Stream _stream; private readonly VinnoStreamReader _reader; private readonly VinnoStreamWriter _writer; private readonly string _filePath; private readonly List _imagePositionList; private bool _disposed; private bool _closed; private readonly object _addGetLocker = new object(); private const string Header = "VINNO IMAGE DATA"; private const string CacheHeader = "VINNOIMGCACHE"; /// /// Gets the version of this image data. /// public int Version { get; } /// /// Gets the image count of this image data. /// public int ImageCount { get; private set; } /// /// Gets the probe information. /// public VinnoProbe Probe { get; private set; } /// /// Gets the image format of this image data. /// public VidImageFormat ImageFormat { get; } /// /// Gets or sets the extended data. /// public byte[] ExtendedData { get; set; } /// /// Create a VINNO Image Data. /// /// The file path to read or create. /// The operation mode, create to create a vid file, open to open a vid file. public VinnoImageData(string filePath, OperationMode mode) { try { ImageFormat = VidImageFormat.Jpeg; _filePath = filePath; _operationMode = mode; ExtendedData = new byte[0]; if (mode == OperationMode.Create) { //Create temp file. var tempFile = _filePath + ".tmp"; _stream = new FileStream(tempFile, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite); _writer = new VinnoStreamWriter(_stream); Version = 3; Probe = null; _imagePositionList = new List(); ImageCount = 0; } else { if (!File.Exists(_filePath) && File.Exists(_filePath + ".tmp")) { _filePath = _filePath + ".tmp"; } _stream = new FileStream(_filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite); _reader = new VinnoStreamReader(_stream); var header = _reader.ReadString(); if (header != Header && header != CacheHeader) { _stream.Dispose(); throw new InvalidDataException("File is not a VID file."); } Version = _reader.ReadInt(); //Get probe info var probeData = _reader.ReadBytes(); Probe = VinnoProbe.FromBytes(probeData); ImageFormat = (VidImageFormat)_reader.ReadInt(); ExtendedData = _reader.ReadBytes(); //Get the index list. _imagePositionList = new List(_reader.ReadLongs()); ImageCount = _imagePositionList.Count; } } catch (Exception ex) { _stream?.Dispose(); } } /// /// Create a VINNO Image Data. /// /// public VinnoImageData(Stream stream) { try { ImageFormat = VidImageFormat.Jpeg; ExtendedData = new byte[0]; _stream = stream; _reader = new VinnoStreamReader(_stream); var header = _reader.ReadString(); if (header != Header) { _stream.Dispose(); throw new InvalidDataException("File is not a VID file."); } Version = _reader.ReadInt(); //Get probe info var probeData = _reader.ReadBytes(); Probe = VinnoProbe.FromBytes(probeData); ImageFormat = (VidImageFormat)_reader.ReadInt(); ExtendedData = _reader.ReadBytes(); //Get the index list. _imagePositionList = new List(_reader.ReadLongs()); ImageCount = _imagePositionList.Count; } catch (Exception ex) { _stream?.Dispose(); } } /// /// Add probe info into vid. /// /// public void AddProbe(VinnoProbe probe) { Probe = probe; } /// /// Add dicom info into vid /// /// public void AddExtendedData(byte[] extendedData) { ExtendedData = extendedData; } /// /// Add one image into vid. /// /// public void AddImage(VinnoImage image) { if (_closed) { throw new InvalidOperationException("ImageData closed."); } if (_operationMode != OperationMode.Create) { throw new InvalidOperationException("Can not add image under open mode."); } lock (_addGetLocker) { var postion = _stream.Position; _writer.WriteBytes(image.ToBytes()); _imagePositionList.Add(postion); ImageCount++; } } /// /// Gets one image from the vid. /// /// /// public VinnoImage GetImage(int index) { if (_closed) { throw new InvalidOperationException("ImageData closed."); } if (_operationMode != OperationMode.Open) { throw new InvalidOperationException("Can not open image under create mode."); } if (index >= ImageCount || index < 0) { throw new IndexOutOfRangeException("Can not find image Data"); } lock (_addGetLocker) { //Jump to image. _stream.Position = _imagePositionList[index]; var imageData = _reader.ReadBytes(); return VinnoImage.FromBytes(imageData); } } public void Close() { if (!_closed) { lock (_addGetLocker) { if (_operationMode == OperationMode.Create && !string.IsNullOrWhiteSpace(_filePath) && ImageCount > 0) { using (var stream = File.Create(_filePath)) { var writer = new VinnoStreamWriter(stream); writer.WriteString(Header); writer.WriteInt(Version); writer.WriteBytes(Probe.ToBytes()); writer.WriteInt((int)ImageFormat); writer.WriteBytes(ExtendedData); //Position data length = imagecount(int 4bytes) + imagecount * positionSize(long 8bytes) var positionsDataLength = _imagePositionList.Count * sizeof(long) + 4; var offset = stream.Length + positionsDataLength; for (var i = 0; i < _imagePositionList.Count; i++) { _imagePositionList[i] = _imagePositionList[i] + offset; } writer.WriteLongs(_imagePositionList.ToArray()); _stream.Position = 0; _stream.CopyTo(stream); } } _stream?.Dispose(); if (_operationMode == OperationMode.Create && !string.IsNullOrWhiteSpace(_filePath)) { var tempFile = _filePath + ".tmp"; File.Delete(tempFile); } } _closed = true; } } protected void Dispose(bool disposing) { if (!_disposed) { Close(); } _disposed = true; } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } } /// /// 扫描方位 左侧或右侧 /// public enum ScanPosition { Left, Right, Fusion } public class Vinno3DPhysicalData { /// /// Get thyroid scan position. /// public ScanPosition ScanPosition { get; } /// /// Get vinno visual. /// public IList VinnoVisuals { get; } /// /// Get vinno visual. /// public VinnoProbe VinnoProbe { get; } public Vinno3DPhysicalData(ScanPosition scanPosition, VinnoProbe vinnoProbe, IList vinnoVisuals) { ScanPosition = scanPosition; VinnoProbe = vinnoProbe; VinnoVisuals = vinnoVisuals; } /// /// From bytes. /// /// bytes. /// Instance of vinno carotid 3d physical data. public static Vinno3DPhysicalData FromBytes(byte[] bytes) { using (var stream = new MemoryStream(bytes)) { stream.Position = 0; var reader = new VinnoStreamReader(stream); var thyroidType = (ScanPosition)reader.ReadByte(); var probe = VinnoProbe.FromBytes(reader.ReadBytes()); var visualCount = reader.ReadByte(); var visuals = new List(); for (int i = 0; i < visualCount; i++) { var visual = VinnoVisual.FromBytes(reader.ReadBytes()); // 全部为null visuals.Add(visual); } return new Vinno3DPhysicalData(thyroidType, probe, visuals); } } } }