Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
3314b08
Support imzML file format
YukiMatsuzawa Apr 21, 2026
915bd0b
Add dims accumulate data provider
YukiMatsuzawa Apr 28, 2026
90428ce
Refactor to async pixel intensity loading in imaging
YukiMatsuzawa May 12, 2026
adbe86c
Refactor AccumulatedIntensity calculation and binding
YukiMatsuzawa May 13, 2026
d6023e2
Refactor image loading with async cache placeholder
YukiMatsuzawa May 13, 2026
64ba746
Fix dims imaging
YukiMatsuzawa May 15, 2026
2beb18f
Update image title handling and display in imaging view
YukiMatsuzawa May 21, 2026
8001e30
Fix dims imaging mass chromatogram
YukiMatsuzawa May 25, 2026
6292da2
Fix save method
YukiMatsuzawa May 26, 2026
00b1a43
Add dims imaging ribbon
YukiMatsuzawa May 28, 2026
9f0c5c3
Add ExportIntensities feature and update export bindings
YukiMatsuzawa Jun 24, 2026
e7e263b
Update RawDataHandler version
YukiMatsuzawa Jul 22, 2026
3e0f444
Potential fix for pull request finding
YukiMatsuzawa Jul 23, 2026
c9f1742
Potential fix for pull request finding
YukiMatsuzawa Jul 23, 2026
e6dcb98
Potential fix for pull request finding
YukiMatsuzawa Jul 23, 2026
b8c6fb7
Manage imaging pixel loader lifecycles
YukiMatsuzawa Jul 28, 2026
78adf23
Simplify imaging intensity models
YukiMatsuzawa Jul 28, 2026
b80c969
Remove unused imaging laser info
YukiMatsuzawa Jul 29, 2026
add2e14
Move pixel intensity export to image results
YukiMatsuzawa Jul 29, 2026
810d2b7
Make raw pixel reset asynchronous
YukiMatsuzawa Jul 29, 2026
64c5167
Potential fix for pull request finding
YukiMatsuzawa Jul 29, 2026
62ea547
Honor cancellation when saving imaging ROIs
YukiMatsuzawa Jul 29, 2026
44a743a
Potential fix for pull request finding
YukiMatsuzawa Jul 29, 2026
0f72500
Merge branch 'feature/imzml-imaging' of https://github.com/systemsomi…
YukiMatsuzawa Jul 29, 2026
e48a475
Handle missing ROI intensity in tooltips
YukiMatsuzawa Jul 29, 2026
1817b7d
Update targetframeworks
YukiMatsuzawa Jul 30, 2026
f30a354
change publish script
YukiMatsuzawa Jul 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/dotnet_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ jobs:
- name: Restore packages
run: dotnet restore /property:Configuration="Debug vendor unsupported"
- name: Publish
run: dotnet publish src/MSDIAL4/MsDial/MSDIAL.csproj -o artifact --configuration "Release vendor unsupported" --framework net48 -p:SkipLibraryDownload=true
run: dotnet publish src/MSDIAL4/MsDial/MSDIAL.csproj -o artifact --configuration "Release vendor unsupported" --framework net48 -p:SkipLibraryDownload=true -p:ErrorOnDuplicatePublishOutputFiles=false
- name: Copy licenses
run: Copy-Item -Path LGPL.txt,THIRD-PARTY-LICENSE-README.md,README.md -Destination artifact
- name: Upload
Expand Down
Binary file not shown.
Binary file not shown.
65 changes: 65 additions & 0 deletions src/Common/CommonStandard/DataStructure/ConcurrentLruCache.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using System.Collections.Generic;
using System.Threading;

namespace CompMs.Common.DataStructure;

public sealed class ConcurrentLruCache<TKey, TValue>
{
private readonly LruCache<TKey, TValue> _cache;
private readonly ReaderWriterLockSlim _locker;

public ConcurrentLruCache(int capacity, IEqualityComparer<TKey> comparer) {
_cache = new LruCache<TKey, TValue>(capacity, comparer);
_locker = new();
}

public ConcurrentLruCache(int capacity) {
_cache = new LruCache<TKey, TValue>(capacity);
_locker = new();
}

public TValue Get(TKey key) {
_locker.EnterWriteLock();
try {
return _cache.Get(key);
}
finally {
_locker.ExitWriteLock();
}
}
Comment thread
Copilot marked this conversation as resolved.

public void Put(TKey key, TValue value) {
_locker.EnterWriteLock();
try {
_cache.Put(key, value);
}
finally {
_locker.ExitWriteLock();
}
}

public bool ContainsKey(TKey key) {
_locker.EnterReadLock();
try {
return _cache.ContainsKey(key);
}
finally {
_locker.ExitReadLock();
}
}

public bool TryGet(TKey key, out TValue value) {
_locker.EnterWriteLock();
try {
if (_cache.ContainsKey(key)) {
value = _cache.Get(key);
return true;
}
value = default!;
return false;
}
finally {
_locker.ExitWriteLock();
}
}
Comment thread
Copilot marked this conversation as resolved.
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<TargetFrameworks>netstandard2.0;net48</TargetFrameworks>
<AssemblyName>MsdialGcmsProcess</AssemblyName>
<Configurations>Debug;Release;Debug vendor unsupported;Release vendor unsupported</Configurations>
</PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<TargetFrameworks>netstandard2.0;net48</TargetFrameworks>
<AssemblyName>MsdialLcmsProcess</AssemblyName>
<Configurations>Debug;Release;Debug vendor unsupported;Release vendor unsupported</Configurations>
</PropertyGroup>
Expand Down
2 changes: 1 addition & 1 deletion src/MSDIAL5/MsdialCore/Enum/SupportFormat.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
using System.Text;

namespace CompMs.MsdialCore.Enum {
public enum SupportMsRawDataExtension { abf, ibf, cdf, mzml, wiff, raw, d, wiff2, qgd, lcd, lrp }
public enum SupportMsRawDataExtension { abf, ibf, cdf, mzml, wiff, raw, d, wiff2, qgd, lcd, lrp, imzml }
public enum MsdialDataStorageFormat { dcl, pai, arf, aef, msf, bfasta, bpep, spep, prf, rtc }
}
8 changes: 4 additions & 4 deletions src/MSDIAL5/MsdialCore/MsdialCore.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@
<PackageReference Include="Accord" Version="3.8.0" />
<PackageReference Include="Accord.Math" Version="3.8.0" />
<PackageReference Include="Accord.Statistics" Version="3.8.0" />
<PackageReference Include="RawDataHandler" Version="1.2.9225.202" Condition="'$(Configuration)'=='Release'" />
<PackageReference Include="RawDataHandler" Version="1.2.*-*" Condition="'$(Configuration)'=='Debug'" />
<PackageReference Include="RawDataHandler-Vendor-UnSupported" Version="1.2.*-*" Condition="'$(Configuration)'=='Debug vendor unsupported'" />
<PackageReference Include="RawDataHandler-Vendor-UnSupported" Version="1.2.*" Condition="'$(Configuration)'=='Release vendor unsupported'" />
<PackageReference Include="RawDataHandler" Version="1.3.9699.469" Condition="'$(Configuration)'=='Release'" />
<PackageReference Include="RawDataHandler" Version="1.3.*-*" Condition="'$(Configuration)'=='Debug'" />
<PackageReference Include="RawDataHandler-Vendor-UnSupported" Version="1.3.*-*" Condition="'$(Configuration)'=='Debug vendor unsupported'" />
<PackageReference Include="RawDataHandler-Vendor-UnSupported" Version="1.3.*" Condition="'$(Configuration)'=='Release vendor unsupported'" />
<PackageReference Include="System.Data.SQLite.Core" Version="1.0.112.2" />
<PackageReference Include="System.IO.Compression" Version="4.3.0" />
<PackageReference Include="System.Net.Http" Version="4.3.4" />
Expand Down
107 changes: 107 additions & 0 deletions src/MSDIAL5/MsdialDimsCore/Algorithm/DimsDataProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,82 @@ public int BinningMz(double mz) {
}
}

public class DimsAccumulateDataProvider : BaseDataProvider
{
public DimsAccumulateDataProvider(IEnumerable<RawSpectrum> spectrums, double massTolerance, double timeBegin, double timeEnd)
: base(AccumulateRawSpectrums(
spectrums
.Where(spec => timeBegin <= spec.ScanStartTime && spec.ScanStartTime <= timeEnd)
.Select(spec => spec.ShallowCopy())
.ToArray(),
massTolerance)) {

}

public DimsAccumulateDataProvider(RawMeasurement rawObj, double massTolerance, double timeBegin, double timeEnd)
: this(rawObj.SpectrumList, massTolerance, timeBegin, timeEnd) {

}

public DimsAccumulateDataProvider(AnalysisFileBean file, double massTolerance, double timeBegin, double timeEnd, bool isGuiProcess = false, int retry = 5)
: this(LoadMeasurement(file, true, false, isGuiProcess, retry).SpectrumList, massTolerance, timeBegin, timeEnd) {

}


private static List<RawSpectrum> AccumulateRawSpectrums(RawSpectrum[] spectrums, double massTolerance) {
var targetmz4ppmcalc = 500.0;
var binning = new Binning(targetmz4ppmcalc, massTolerance);
var ms1Spectrums = spectrums.Where(spectrum => spectrum.MsLevel == 1).ToList();
var groups = ms1Spectrums.SelectMany(spectrum => spectrum.Spectrum)
.GroupBy(peak => binning.BinningMz(peak.Mz));
var massBins = new Dictionary<int, double[]>();
foreach (var group in groups) {
var peaks = group.ToList();
var accIntensity = peaks.Sum(peak => peak.Intensity);
var basepeak = peaks.Argmax(peak => peak.Intensity);
massBins[group.Key] = new double[] { basepeak.Mz, accIntensity, basepeak.Intensity };
}
var result = ms1Spectrums.First().ShallowCopy();
SpectrumParser.setSpectrumProperties(result, massBins, isSortMz: true);
var results = new[] { result }.Concat(spectrums.Where(spectrum => spectrum.MsLevel != 1)).ToList();
for (int i = 0; i < results.Count; i++) {
results[i].Index = i;
}
return results;
}
Comment on lines +178 to +198

class Binning
{
public double Pivot { get; }
public double Tolerance { get; }
public double Ppm { get; }
public double LogTolerance { get; }

public Binning(double pivot, double tolerance) {
Pivot = pivot;
Tolerance = tolerance;
Ppm = tolerance / Pivot * 1_000_000d;
LogTolerance = Math.Log(1 + Ppm / 1_000_000d);
}

/// <summary>
/// Calculates the bin index for a given m/z value.
/// </summary>
/// <param name="mz">The mass-to-charge (m/z) value.</param>
/// <returns>The bin index as an integer.</returns>
public int BinningMz(double mz) {
if (mz <= Pivot) {
return (int)(mz / Tolerance);
}
else {
int pivotbin = (int)(Pivot / Tolerance) + 1;
return pivotbin + (int)(Math.Log(mz / Pivot) / LogTolerance);
}
}
}
}

public class DimsBpiDataProviderFactory
: IDataProviderFactory<AnalysisFileBean>, IDataProviderFactory<RawMeasurement>
{
Expand Down Expand Up @@ -238,4 +314,35 @@ public IDataProvider Create(RawMeasurement source) {
return new DimsAverageDataProvider(source, massTolerance, timeBegin, timeEnd);
}
}

public class DimsAccumulateDataProviderFactory
: IDataProviderFactory<AnalysisFileBean>, IDataProviderFactory<RawMeasurement>
{
private readonly double massTolerance;
private readonly double timeBegin;
private readonly double timeEnd;
private readonly int retry;
private readonly bool isGuiProcess;

public DimsAccumulateDataProviderFactory(
double massTolerance,
double timeBegin = double.MinValue,
double timeEnd = double.MaxValue,
int retry = 5,
bool isGuiProcess = false) {
this.massTolerance = massTolerance;
this.timeBegin = timeBegin;
this.timeEnd = timeEnd;
this.retry = retry;
this.isGuiProcess = isGuiProcess;
}

public IDataProvider Create(AnalysisFileBean source) {
return new DimsAccumulateDataProvider(source, massTolerance, timeBegin, timeEnd, isGuiProcess, retry);
}

public IDataProvider Create(RawMeasurement source) {
return new DimsAccumulateDataProvider(source, massTolerance, timeBegin, timeEnd);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ namespace CompMs.MsdialDimsCore.Parameter
[Union(0, typeof(DimsBpiDataProviderFactoryParameter))]
[Union(1, typeof(DimsTicDataProviderFactoryParameter))]
[Union(2, typeof(DimsAverageDataProviderFactoryParameter))]
[Union(3, typeof(DimsAccumulateDataProviderFactoryParameter))]
public interface IDimsDataProviderFactoryParameter
{
IDataProviderFactory<AnalysisFileBean> Create(int retry, bool isGuiProcess);
Expand Down Expand Up @@ -84,4 +85,29 @@ public IDataProviderFactory<RawMeasurement> Create() {
return new DimsAverageDataProviderFactory(MassTolerance, TimeBegin, TimeEnd);
}
}

[MessagePackObject]
public class DimsAccumulateDataProviderFactoryParameter: IDimsDataProviderFactoryParameter
{
public DimsAccumulateDataProviderFactoryParameter(double timeBegin, double timeEnd, double massTolerance) {
TimeBegin = timeBegin;
TimeEnd = timeEnd;
MassTolerance = massTolerance;
}

[Key(nameof(TimeBegin))]
public double TimeBegin { get; }
[Key(nameof(TimeEnd))]
public double TimeEnd { get; }
[Key(nameof(MassTolerance))]
public double MassTolerance { get; }

public IDataProviderFactory<AnalysisFileBean> Create(int retry, bool isGuiProcess) {
return new DimsAccumulateDataProviderFactory(MassTolerance, TimeBegin, TimeEnd, retry, isGuiProcess);
}

public IDataProviderFactory<RawMeasurement> Create() {
return new DimsAccumulateDataProviderFactory(MassTolerance, TimeBegin, TimeEnd);
}
}
}
2 changes: 1 addition & 1 deletion src/MSDIAL5/MsdialDimsCore/ProcessFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ public async Task RunAsync(AnalysisFileBean file, ProcessOption option, IProgres
// faeture detections
Console.WriteLine("Peak picking started");
var ms1Spectrum = provider.LoadMs1Spectrums().Argmax(spec => spec.Spectrum.Length);
var chromPeaks = DataAccess.ConvertRawPeakElementToChromatogramPeakList(ms1Spectrum.Spectrum);
var chromPeaks = DataAccess.ConvertRawPeakElementToChromatogramPeakList(ms1Spectrum.Spectrum, param.MassRangeBegin, param.MassRangeEnd);
var sChromPeaks = new Chromatogram(chromPeaks, ChromXType.Mz, ChromXUnit.Mz).ChromatogramSmoothing(param.SmoothingMethod, param.SmoothingLevel).AsPeakArray();

var peakPickResults = PeakDetection.PeakDetectionVS1(sChromPeaks, param.MinimumDatapoints, param.MinimumAmplitude);
Expand Down
40 changes: 37 additions & 3 deletions src/MSDIAL5/MsdialGuiApp/Model/Chart/BitmapImageModel.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using CompMs.CommonMVVM;
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Media;
using System.Windows.Media.Imaging;

Expand All @@ -9,8 +11,9 @@ internal sealed class BitmapImageModel : BindableBase
{
public static readonly int ImageMargin = 1;

private readonly SemaphoreSlim _semaphoreSlim = new(1, 1);

private Func<BitmapSource>? _bitmapSourceFactory;
private BitmapSource? _bitmapSource;

public BitmapImageModel(BitmapSource bitmapSource, string title) {
_bitmapSource = bitmapSource;
Expand All @@ -22,8 +25,39 @@ public BitmapImageModel(Func<BitmapSource> bitmapSourceFactory, string title) {
Title = title;
}

public string Title { get; }
public BitmapSource BitmapSource => _bitmapSource ?? _bitmapSourceFactory!.Invoke();
public string Title { get; internal set; }

public BitmapSource? BitmapSource {
get => _bitmapSource;
private set => SetProperty(ref _bitmapSource, value);
}
private BitmapSource? _bitmapSource;

public bool LoadingBitmap {
get => _loadingBitmap;
private set => SetProperty(ref _loadingBitmap, value);
}
private bool _loadingBitmap = false;

public async Task EnsureBitmapSourceAsync() {
if (_bitmapSource is not null || LoadingBitmap) {
return;
}

await _semaphoreSlim.WaitAsync().ConfigureAwait(false);

try {
if (_bitmapSource is not null || LoadingBitmap) {
return;
}
LoadingBitmap = true;
BitmapSource = await Task.Run(() => _bitmapSourceFactory!.Invoke()).ConfigureAwait(false);
}
finally {
LoadingBitmap = false;
_semaphoreSlim.Release();
}
}
Comment thread
Copilot marked this conversation as resolved.

public BitmapImageModel WithPalette(BitmapPalette palette) {
if (_bitmapSource is null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ public bool UseAverageMs1 {
}
private bool useAverageMs1 = false;

public bool UseAccumulateMs1 {
get => useAccumulateMs1;
set => SetProperty(ref useAccumulateMs1, value);
}
private bool useAccumulateMs1 = false;

public double TimeBegin {
get => timeBegin;
set => SetProperty(ref timeBegin, value);
Expand Down Expand Up @@ -65,6 +71,9 @@ public IDimsDataProviderFactoryParameter CreateDataProviderFactoryParameter() {
else if (UseAverageMs1) {
return new DimsAverageDataProviderFactoryParameter(TimeBegin, TimeEnd, MassTolerance);
}
else if (UseAccumulateMs1) {
return new DimsAccumulateDataProviderFactoryParameter(TimeBegin, TimeEnd, MassTolerance);
}
throw new NotSupportedException();
}

Expand Down Expand Up @@ -92,6 +101,13 @@ private void PrepareProviderFactoryParameter(IDimsDataProviderFactoryParameter f
TimeEnd = averageParameter.TimeEnd;
MassTolerance = averageParameter.MassTolerance;
break;
case DimsAccumulateDataProviderFactoryParameter accumulateParameter:
UseMs1WithHighestTic = false;
UseAccumulateMs1 = true;
TimeBegin = accumulateParameter.TimeBegin;
TimeEnd = accumulateParameter.TimeEnd;
MassTolerance = accumulateParameter.MassTolerance;
break;
Comment on lines +104 to +110
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@

internal interface IWholeImageResultModel
{
public IntensityImageModel? SelectedPeakIntensities { get; }
public IntensityImagePlaceholderModel IntensityImagePlaceholder { get; }
}
Loading
Loading