Skip to content

Commit 23be03d

Browse files
sebgodclaude
andcommitted
v4.2.0: Add GetChannel/ChannelCount API for ergonomic channel access
ImageHDU and ImageData now expose: - ChannelCount: number of channels (1 for 2D, N for 3D+) - GetChannel(int index): returns a single channel as a rectangular array (e.g. float[,]), works for both 2D and 3D+ images Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 453df63 commit 23be03d

4 files changed

Lines changed: 119 additions & 42 deletions

File tree

CSharpFITS/CSharpFITS.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@
1111
<LangVersion>9.0</LangVersion>
1212
<PackageReadmeFile>README.md</PackageReadmeFile>
1313
<PackageLicenseFile>license.txt</PackageLicenseFile>
14-
<AssemblyVersion>4.1.0.0</AssemblyVersion>
15-
<FileVersion>4.1.0.0</FileVersion>
14+
<AssemblyVersion>4.2.0.0</AssemblyVersion>
15+
<FileVersion>4.2.0.0</FileVersion>
1616
<PackageTags>Astronomy,FITS,Image</PackageTags>
1717
<RepositoryUrl>https://github.com/SharpAstro/FITS.Lib</RepositoryUrl>
1818
<PackageProjectUrl>https://github.com/SharpAstro/FITS.Lib</PackageProjectUrl>
1919
<Description>Supports reading and writing FITS files</Description>
20-
<Version>4.1.0</Version>
20+
<Version>4.2.0</Version>
2121
<Authors>Virtual Observatory - India</Authors>
2222
<PackageId>FITS.Lib</PackageId>
2323
<RootNamespace>nom.tam</RootNamespace>

CSharpFITS/fits/ImageData.cs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,49 @@ public override Object DataArray
6565
/// </summary>
6666
public virtual ImageTiler Tiler => tiler;
6767

68+
/// <summary>The number of channels (planes) in the image.
69+
/// For 2D images this is 1. For 3D images this is the size of the first axis (NAXIS3).</summary>
70+
public int ChannelCount
71+
{
72+
get
73+
{
74+
var data = DataArray;
75+
if (data == null) return 0;
76+
int[] dims = ArrayFuncs.GetDimensions(data);
77+
return dims.Length <= 2 ? 1 : dims[0];
78+
}
79+
}
80+
81+
/// <summary>Get a single channel (plane) of image data as a rectangular array.
82+
/// For 2D images, index must be 0 and returns the full image.
83+
/// For 3D images, returns the Nth channel as a rectangular 2D array.
84+
/// </summary>
85+
/// <param name="index">The zero-based channel index.</param>
86+
/// <returns>A rectangular array (e.g. float[,]) for the requested channel.</returns>
87+
/// <exception cref="IndexOutOfRangeException">If the index is out of range.</exception>
88+
/// <exception cref="InvalidOperationException">If image data is not available.</exception>
89+
public Array GetChannel(int index)
90+
{
91+
var data = DataArray;
92+
if (data == null)
93+
throw new InvalidOperationException("Image data is not available.");
94+
95+
if (data is Array outerArray && ArrayFuncs.IsArrayOfArrays(data))
96+
{
97+
// 3D+ hybrid: jagged outer array of rectangular inner arrays
98+
if (index < 0 || index >= outerArray.Length)
99+
throw new IndexOutOfRangeException(
100+
$"Channel index {index} is out of range [0, {outerArray.Length}).");
101+
return (Array)outerArray.GetValue(index);
102+
}
103+
104+
// 2D rectangular or 1D array
105+
if (index != 0)
106+
throw new IndexOutOfRangeException(
107+
$"Channel index {index} is out of range for a {ArrayFuncs.CountDimensions(data)}D image.");
108+
return (Array)data;
109+
}
110+
68111
/// <summary>The size of the data
69112
/// </summary>
70113
internal long byteSize;

CSharpFITS/fits/ImageHDU.cs

Lines changed: 50 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,27 @@
1-
namespace nom.tam.fits
1+
namespace nom.tam.fits
22
{
3-
/*
4-
* Copyright: Thomas McGlynn 1997-2007.
5-
* Many thanks to David Glowacki (U. Wisconsin) for substantial
6-
* improvements, enhancements and bug fixes.
7-
* The CSharpFITS package is a C# port of Tom McGlynn's
8-
* nom.tam.fits Java package, initially ported by Samuel Carliles
9-
*
10-
* Copyright: 2007 Virtual Observatory - India.
11-
*
12-
* Use is subject to license terms
3+
/*
4+
* Copyright: Thomas McGlynn 1997-2007.
5+
* Many thanks to David Glowacki (U. Wisconsin) for substantial
6+
* improvements, enhancements and bug fixes.
7+
* The CSharpFITS package is a C# port of Tom McGlynn's
8+
* nom.tam.fits Java package, initially ported by Samuel Carliles
9+
*
10+
* Copyright: 2007 Virtual Observatory - India.
11+
*
12+
* Use is subject to license terms
1313
*/
1414
using System;
1515
using util;
16-
using image;
17-
/// <summary>FITS image header/data unit.The ImageTiler class allows
18-
/// users to extract subimages from a FITS primary image or image extension.</summary>
16+
using image;
17+
/// <summary>FITS image header/data unit.The ImageTiler class allows
18+
/// users to extract subimages from a FITS primary image or image extension.</summary>
1919
public class ImageHDU : BasicHDU
20-
{
20+
{
2121
/// <summary>Indicate that Images can appear at the beginning of a FITS dataset</summary>
2222
internal override bool CanBePrimary => true;
2323

24-
/// <summary>Change the Image from/to primary</summary>
24+
/// <summary>Change the Image from/to primary</summary>
2525
internal override bool PrimaryHDU
2626
{
2727
set
@@ -44,23 +44,34 @@ internal override bool PrimaryHDU
4444
myHeader.Xtension = "IMAGE";
4545
}
4646
}
47-
}
47+
}
4848
/// <summary>
4949
/// returns ImageTiler instance
5050
/// </summary>
5151
public virtual ImageTiler Tiler => ((ImageData)myData).Tiler;
5252

53-
/// <summary>Build an image HDU using the supplied data.</summary>
54-
/// <param name="obj">the data used to build the image.</param>
55-
/// <exception cref="FitsException">If there was a problem with the data.</exception>
53+
/// <summary>The number of channels (planes) in the image.
54+
/// For 2D images this is 1. For 3D images this is the size of the first axis.</summary>
55+
public int ChannelCount => ((ImageData)myData).ChannelCount;
56+
57+
/// <summary>Get a single channel (plane) of image data as a rectangular array.
58+
/// For 2D images, index must be 0 and returns the full image.
59+
/// For 3D images, returns the Nth channel as a rectangular 2D array (e.g. float[,]).
60+
/// </summary>
61+
/// <param name="index">The zero-based channel index.</param>
62+
public Array GetChannel(int index) => ((ImageData)myData).GetChannel(index);
63+
64+
/// <summary>Build an image HDU using the supplied data.</summary>
65+
/// <param name="obj">the data used to build the image.</param>
66+
/// <exception cref="FitsException">If there was a problem with the data.</exception>
5667
public ImageHDU(Header h, Data d)
5768
{
5869
myData = d;
5970
myHeader = h;
6071
}
6172

62-
/// <summary>Check that this HDU has a valid header for this type.</summary>
63-
/// <returns> <CODE>true</CODE> if this HDU has a valid header.</returns>
73+
/// <summary>Check that this HDU has a valid header for this type.</summary>
74+
/// <returns> <CODE>true</CODE> if this HDU has a valid header.</returns>
6475
public new static bool IsHeader(Header hdr)
6576
{
6677
bool found = false;
@@ -83,20 +94,20 @@ public ImageHDU(Header h, Data d)
8394
return !hdr.GetBooleanValue("GROUPS");
8495
}
8596

86-
/// <summary>Check if this object can be described as a FITS image.</summary>
87-
/// <param name="o">The Object being tested.</param>
97+
/// <summary>Check if this object can be described as a FITS image.</summary>
98+
/// <param name="o">The Object being tested.</param>
8899
public static bool IsData(Object o)
89100
{
90101
// Removed multidimension Array check logic: "(typeof(Array).Equals(o.GetType()) || o.GetType().FullName.IndexOf("[")!=-1)"
91102

92103
// return (o != null) && o.GetType().IsArray && (!ArrayFuncs.GetBaseClass(o).Equals(typeof(bool)));
93104

94-
return (o != null) && o.GetType().IsArray && (ArrayFuncs.GetBaseClass(o).IsPrimitive && !(ArrayFuncs.GetBaseClass(o).Equals(typeof(bool))));
95-
}
96-
97-
/// <summary>Create a Data object to correspond to the header description.</summary>
98-
/// <returns> An unfilled Data object which can be used to read in the data for this HDU.</returns>
99-
/// <exception cref=""> FitsException if the image extension could not be created.</exception>
105+
return (o != null) && o.GetType().IsArray && (ArrayFuncs.GetBaseClass(o).IsPrimitive && !(ArrayFuncs.GetBaseClass(o).Equals(typeof(bool))));
106+
}
107+
108+
/// <summary>Create a Data object to correspond to the header description.</summary>
109+
/// <returns> An unfilled Data object which can be used to read in the data for this HDU.</returns>
110+
/// <exception cref=""> FitsException if the image extension could not be created.</exception>
100111
internal override Data ManufactureData()
101112
{
102113
return ManufactureData(myHeader);
@@ -107,9 +118,9 @@ public static Data ManufactureData(Header hdr)
107118
return new ImageData(hdr);
108119
}
109120

110-
/// <summary>Create a header that describes the given image data.</summary>
111-
/// <param name="o">The image to be described.</param>
112-
/// <exception cref=""> FitsException if the object does not contain valid image data.</exception>
121+
/// <summary>Create a header that describes the given image data.</summary>
122+
/// <param name="o">The image to be described.</param>
123+
/// <exception cref=""> FitsException if the object does not contain valid image data.</exception>
113124
public static Header ManufactureHeader(Data d)
114125
{
115126
if (d == null)
@@ -123,13 +134,13 @@ public static Header ManufactureHeader(Data d)
123134
return h;
124135
}
125136

126-
/// <summary>Encapsulate an object as an ImageHDU.</summary>
137+
/// <summary>Encapsulate an object as an ImageHDU.</summary>
127138
public static Data Encapsulate(Object o)
128139
{
129140
return new ImageData(o);
130-
}
131-
132-
/// <summary>Print out some information about this HDU.</summary>
141+
}
142+
143+
/// <summary>Print out some information about this HDU.</summary>
133144
public override void Info()
134145
{
135146
if (IsHeader(myHeader))
@@ -167,5 +178,5 @@ public override void Info()
167178
Console.Out.WriteLine(" Unable to get data");
168179
}
169180
}
170-
}
171-
}
181+
}
182+
}

IMPROVEMENTS.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,29 @@
22

33
Changes since forking from [rwg0/csharpfits](https://github.com/rwg0/csharpfits).
44

5+
## v4.2.0
6+
7+
### Hybrid arrays for 3D+ images and channel access API
8+
9+
For images with 3 or more dimensions (e.g. multi-channel), `DataArray` now returns a
10+
hybrid structure: a jagged outer `Array[]` of rectangular inner arrays. For example,
11+
a 3-channel 4176x6248 float image returns `Array[3]` of `float[4176, 6248]` instead of
12+
a single `float[3, 4176, 6248]`. This reduces the maximum single allocation from 299MB
13+
to ~100MB, which is much friendlier to the GC and Large Object Heap.
14+
15+
1D and 2D images are unchanged (return `float[]` or `float[,]` respectively).
16+
17+
New API for convenient channel access:
18+
19+
```csharp
20+
ImageHDU hdu = ...;
21+
int n = hdu.ChannelCount; // 1 for 2D, N for 3D+
22+
float[,] red = (float[,])hdu.GetChannel(0); // first channel
23+
float[,] green = (float[,])hdu.GetChannel(1); // second channel
24+
```
25+
26+
Also available on `ImageData` directly: `ChannelCount` and `GetChannel(int index)`.
27+
528
## v4.1.0
629

730
### SIMD write path and buffered stream bypass

0 commit comments

Comments
 (0)