Skip to content
Snippets Groups Projects
Unverified Commit 68f2a3fc authored by xgui3783's avatar xgui3783 Committed by GitHub
Browse files

Merge pull request #1147 from FZJ-INM1-BDA/hotfix_reenableAutoradiograph

hotfix: reenable autoradiograph
parents 00d13669 08169396
No related branches found
No related tags found
No related merge requests found
......@@ -48,6 +48,10 @@
"input": "worker/worker-nifti.js",
"inject": false,
"bundleName": "worker-nifti"
},{
"input": "worker/worker-typedarray.js",
"inject": false,
"bundleName": "worker-typedarray"
},
{
......
# v2.6.4
## Feature
- Re-enabled autoradiographs for receptor datasets
......@@ -33,6 +33,7 @@ pages:
- Fetching datasets: 'advanced/datasets.md'
- Display non-atlas volumes: 'advanced/otherVolumes.md'
- Release notes:
- v2.6.5: 'releases/v2.6.5.md'
- v2.6.4: 'releases/v2.6.4.md'
- v2.6.3: 'releases/v2.6.3.md'
- v2.6.2: 'releases/v2.6.2.md'
......
{
"name": "interactive-viewer",
"version": "2.6.4",
"version": "2.6.5",
"description": "HBP interactive atlas viewer. Integrating KG query, dataset previews & more. Based on humanbrainproject/nehuba & google/neuroglancer. Built with angular",
"scripts": {
"build-aot": "ng build && node ./third_party/matomo/processMatomo.js",
......
import { Component, Input, OnChanges } from "@angular/core";
import { Component, ElementRef, Input, OnChanges, ViewChild } from "@angular/core";
import { BsFeatureReceptorBase } from "../base";
import { CONST } from 'common/constants'
import { TBSDetail } from "../type";
import { environment } from 'src/environments/environment'
import { AtlasWorkerService } from "src/atlasViewer/atlasViewer.workerService.service";
const { RECEPTOR_AR_CAPTION } = CONST
......@@ -27,12 +28,18 @@ export class BsFeatureReceptorAR extends BsFeatureReceptorBase implements OnChan
@Input()
bsLabel: string
public imgUrl: string
@ViewChild('arContainer', { read: ElementRef })
arContainer: ElementRef
constructor(){
private renderBuffer: Uint8ClampedArray
private width: number
private height: number
private pleaseRender = false
constructor(private worker: AtlasWorkerService){
super()
}
ngOnChanges(){
async ngOnChanges(){
this.error = null
this.urls = []
if (!this.bsFeature) {
......@@ -45,16 +52,69 @@ export class BsFeatureReceptorAR extends BsFeatureReceptorBase implements OnChan
}
try {
const url = this.bsFeature.__data.__autoradiographs[this.bsLabel]
if (!url) throw new Error(`autoradiograph cannot be found`)
this.urls = [{ url }]
const query = url.replace('https://object.cscs.ch/v1', '')
this.imgUrl = `${this.DS_PREVIEW_URL}/imageProxy/v1?u=${encodeURIComponent(query)}`
const {
"x-channel": channel,
"x-height": height,
"x-width": width,
content_type: contentType,
content_encoding: contentEncoding,
content,
} = this.bsFeature.__data.__autoradiographs[this.bsLabel]
if (contentType !== "application/octet-stream") {
throw new Error(`contentType expected to be application/octet-stream, but is instead ${contentType}`)
}
if (contentEncoding !== "gzip; base64") {
throw new Error(`contentEncoding expected to be gzip; base64, but is ${contentEncoding} instead.`)
}
const bin = atob(content)
const { pako } = (window as any).export_nehuba
const uint8array: Uint8Array = pako.inflate(bin)
this.width = width
this.height = height
const rgbaBuffer = await this.worker.sendMessage({
method: "PROCESS_TYPED_ARRAY",
param: {
inputArray: uint8array,
width,
height,
channel
},
transfers: [ uint8array.buffer ]
})
this.renderBuffer = rgbaBuffer.result.buffer
this.renderCanvas()
} catch (e) {
this.error = e.toString()
}
}
private renderCanvas(){
if (!this.arContainer) {
this.pleaseRender = true
return
}
const arContainer = (this.arContainer.nativeElement as HTMLElement)
while (arContainer.firstChild) {
arContainer.removeChild(arContainer.firstChild)
}
const canvas = document.createElement("canvas")
canvas.height = this.height
canvas.width = this.width
arContainer.appendChild(canvas)
const ctx = canvas.getContext("2d")
const imgData = ctx.createImageData(this.width, this.height)
imgData.data.set(this.renderBuffer)
ctx.putImageData(imgData, 0, 0)
}
ngAfterViewChecked(){
if (this.pleaseRender) this.renderCanvas()
}
}
\ No newline at end of file
.ar-container > img
/* canvas created by createElement does not have encapsulation applied */
.ar-container >>> canvas
{
width: 100%;
}
\ No newline at end of file
......@@ -16,7 +16,6 @@
<figcaption class="text-muted">
Autoradiograph: {{ RECEPTOR_AR_CAPTION }}
</figcaption>
<div class="ar-container">
<img [src]="imgUrl">
<div class="ar-container" #arContainer>
</div>
</figure>
......@@ -47,7 +47,14 @@ export type TBSDetail = TBSSummary & {
[key: string]: TProfile
}
__autoradiographs: {
[key: string]: string
[key: string]: {
content_type: string
content_encoding: string
['x-width']: number
['x-height']: number
['x-channel']: number
content: string
}
}
__fingerprint: TBSFingerprint
}
......
......@@ -28,10 +28,10 @@ export class AtlasWorkerService {
...data
}, transfers)
const message = await fromEvent(this.worker, 'message').pipe(
filter((message: MessageEvent) => message.data.id && message.data.id === newUuid),
filter((msg: MessageEvent) => msg.data.id && msg.data.id === newUuid),
take(1)
).toPromise()
const { data: returnData } = message as MessageEvent
const { id, error, ...rest } = returnData
if (error) {
......
......@@ -17,7 +17,7 @@ import { MatSnackBar } from "@angular/material/snack-bar";
import { TTemplateImage } from "./interfaces";
export const SIIBRA_API_VERSION_HEADER_KEY='x-siibra-api-version'
export const SIIBRA_API_VERSION = '0.1.9'
export const SIIBRA_API_VERSION = '0.1.10'
const validVolumeType = new Set([
'neuroglancer/precomputed',
......
(function(exports){
exports.typedArray = {
fortranToRGBA(inputArray, width, height, channel) {
if (channel !== 1 && channel !== 3) {
throw new Error(`channel must be either 1 or 3`)
}
const greyScale = (channel === 1)
const dim = width * height
if (channel === 1 && inputArray.length !== dim) {
throw new Error(`for single channel, expect width * height === inputArray.length, but ${width} * ${height} !== ${inputArray.length}`)
}
if (channel === 3 && inputArray.length !== (dim * 3)) {
throw new Error(`for 3 channel, expect 3 * width * height === inputArray.length, but 3 * ${width} * ${height} !== ${inputArray.length}`)
}
const _ = new ArrayBuffer(width * height * 4)
const buffer = new Uint8ClampedArray(_)
for (let i = 0; i < width; i ++) {
for (let j = 0; j < height; j ++) {
for (let k = 0; k < 4; k ++) {
const toIndex = (j * width + i) * 4 + k
const fromValue = k === 3
? 255
: greyScale
? inputArray[width * j + i]
: inputArray[dim * k + width * j + i]
buffer[toIndex] = fromValue
}
}
}
return buffer
}
}
})(
typeof exports === 'undefined'
? self
: exports
)
\ No newline at end of file
......@@ -9,6 +9,7 @@ globalThis.constants = {
if (typeof self.importScripts === 'function') self.importScripts('./worker-plotly.js')
if (typeof self.importScripts === 'function') self.importScripts('./worker-nifti.js')
if (typeof self.importScripts === 'function') self.importScripts('./worker-typedarray.js')
/**
* TODO migrate processing functionalities to other scripts
......@@ -24,11 +25,13 @@ const validTypes = [
const VALID_METHOD = {
PROCESS_PLOTLY: `PROCESS_PLOTLY`,
PROCESS_NIFTI: 'PROCESS_NIFTI',
PROCESS_TYPED_ARRAY: `PROCESS_TYPED_ARRAY`,
}
const VALID_METHODS = [
VALID_METHOD.PROCESS_PLOTLY,
VALID_METHOD.PROCESS_NIFTI,
VALID_METHOD.PROCESS_TYPED_ARRAY,
]
const validOutType = [
......@@ -291,6 +294,27 @@ onmessage = (message) => {
})
}
}
if (message.data.method === VALID_METHOD.PROCESS_TYPED_ARRAY) {
try {
const { inputArray, width, height, channel } = message.data.param
const buffer = self.typedArray.fortranToRGBA(inputArray, width, height, channel)
postMessage({
id,
result: {
buffer
}
}, [ buffer.buffer ])
} catch (e) {
postMessage({
id,
error: {
code: 401,
message: `process typed array error: ${e.toString()}`
}
})
}
}
postMessage({
id,
error: {
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment