<jupyter_start><jupyter_text>Simple Ugly CornerstoneHere we hack cornerstone into Jupyter using just HTML and javascript. This can be easier for a widget and possibly easier to intergrate with other tools like Flask/Dash<jupyter_code>from IPython.display import Javascript, display
from cornerstone_widget.cs_widget import encode_numpy_b64
def fancy_format(in_str, **kwargs):
    """
    A format command for strings that already have lots of curly brackets (ha!)
    :param in_str:
    :param kwargs: the arguments to substitute
    :return:
    >>> fancy_format('{haha} {dan} {bob}', dan = 1, bob = 2)
    '{haha} 1 2'
    """
    new_str = in_str.replace('{', '{{').replace('}', '}}')
    for key in kwargs.keys():
        new_str = new_str.replace('{{%s}}' % key, '{%s}' % key)
    return new_str.format(**kwargs)

import numpy as np<jupyter_output><empty_output><jupyter_text>Javascript SnippetsAll of the javascript snippets required to make the code fly<jupyter_code>PYIMG_JS = """
function str2ab(str) {
    var buf = new ArrayBuffer(str.length*2); // 2 bytes for each char
    var bufView = new Uint16Array(buf);
    var index = 0;
    for (var i=0, strLen=str.length; i<strLen; i+=2) {
        var lower = str.charCodeAt(i);
        var upper = str.charCodeAt(i+1);
        bufView[index] = lower + (upper <<8);
        index++;
    }
    return bufView;
}

function parsePixelData(base64PixelData)
{
    var pixelDataAsString = window.atob(base64PixelData);
    var pixelData = str2ab(pixelDataAsString);
    return pixelData;
}
var imageB64Data = "{image_data}"
var imagePixelData = parsePixelData(imageB64Data);
function getPixelData() {
    return imagePixelData;
}
var image = {
    imageId: 'test',
    minPixelValue: {min_val},
    maxPixelValue: {max_val},
    slope: 1.0,
    intercept: {offset},
    windowCenter : {window_center},
    windowWidth : {window_width},
    getPixelData: getPixelData,
    rows: {height},
    columns: {width},
    height: {height},
    width: {width},
    color: false,
    columnPixelSpacing: .8984375,
    rowPixelSpacing: .8984375,
    sizeInBytes: {width} * {height} * 2
};
"""

zoom_update_js = """
require(['cornerstone-core', 'cornerstoneTools', 'cornerstoneMath'], 
    function(cornerstone, cornerstoneTools, cornerstoneMath){
        var cs_viewer = document.getElementById('{item_name}');
        var viewport = cornerstone.getViewport(cs_viewer);
        viewport.scale += {zoom_factor};
        cornerstone.setViewport(cs_viewer, viewport);
    })
"""
simple_update_js = """
require(['cornerstone-core', 'cornerstoneTools', 'cornerstoneMath'], 
    function(cornerstone, cornerstoneTools, cornerstoneMath){
    var cs_viewer = document.getElementById('{item_name}');
    cornerstoneTools.external.cornerstone = cornerstone
    cornerstoneTools.external.cornerstoneMath = cornerstoneMath
    {simple_numpy_js}
    console.log('Fixing'+cs_viewer+'with'+image);
    var viewport = cornerstone.getDefaultViewportForImage(cs_viewer, image);
    cornerstone.displayImage(cs_viewer, image, viewport);
})
"""
def make_pyimage_js(in_arr):
    # type: (np.ndarray) -> str
    """
    Makes a Cornerstone image from a numpy array
    :param in_arr: numpy array
    :return:
    >>> simple_html = make_pyimage_js(np.eye(3))
    >>> len(simple_html)
    1103
    >>> 'loadPythonImage' in simple_html
    False
    >>> simple_html.split('sizeInBytes')[-1][:11]
    ': 3 * 3 * 2'
    >>> ct_img = 6000*np.eye(7)-3000
    >>> simple_html = make_pyimage_js(ct_img)
    >>> simple_html.split('sizeInBytes')[-1][:11]
    ': 7 * 7 * 2'
    >>> simple_html.split('intercept')[-1][:8]
    ': -3000.'
    >>> simple_html.split('minPixelValue')[-1][:8]
    ': -3000.'
    """
    if len(in_arr.shape) != 2:
        raise NotImplementedError(
            'Does not handle shape:{}'.format(in_arr.shape))
    offset = in_arr.min()
    min_val = offset
    max_val = in_arr.max()
    wc = in_arr.mean()
    ww = in_arr.std()
    new_arr = in_arr.astype(np.float64) - offset
    new_arr = new_arr.astype(np.uint16)
    out_b64 = encode_numpy_b64(new_arr)
    h, w = in_arr.shape
    return fancy_format(PYIMG_JS, window_center=wc, window_width=ww, width=w,
                        height=h, image_data=out_b64,
                        offset=offset, min_val=min_val, max_val=max_val)
import ipywidgets as ipw
size_scroller = ipw.IntSlider(value=128, min=3, max=2048, description='Image Size')
def show_image(obj_id, img_maker):
    c_wid = size_scroller.value
    out_js = fancy_format(
        simple_update_js,
        item_name = obj_id,
        simple_numpy_js = make_pyimage_js(img_maker(c_wid)))
    display(Javascript(out_js))

def zoom_viewer(obj_id, zf):
    out_js = fancy_format(
        zoom_update_js,
        item_name = obj_id, zoom_factor=zf)
    display(Javascript(out_js))
    
noisy_img_but = ipw.Button(description='Noisy Image')
noisy_image = lambda x: np.random.uniform(-1000, 1000, size=(x, x))
noisy_img_but.on_click(lambda *args: show_image('dicom_viewer', noisy_image))
half_img_but = ipw.Button(description='Half Image')
half_image = lambda x: np.eye(x)[:x//2]
half_img_but.on_click(lambda *args: show_image('dicom_viewer', half_image))

zoom_in_but = ipw.Button(description='Zoom In')
zoom_in_but.on_click(lambda *args: zoom_viewer('dicom_viewer', 0.25))
zoom_out_but = ipw.Button(description='Zoom Out')
zoom_out_but.on_click(lambda *args: zoom_viewer('dicom_viewer', -0.25))
simple_cornerstone_view = """
require.config({
  paths: {
      'cornerstone-core': '//unpkg.com/cornerstone-core@2.2.4/dist/cornerstone.min',
      cornerstoneMath: '//unpkg.com/cornerstone-math@0.1.6/dist/cornerstoneMath.min',
      cornerstoneTools: '//unpkg.com/cornerstone-tools@2.3.9/dist/cornerstoneTools.min'
  },
    shim: {
        'cornerstone-core': {
            exports: 'cornerstone',
            deps: ['jquery']
        }
    }
});
function disableContextMenu(e) {
    $(e).on('contextmenu', function(e) {
        e.preventDefault();
    });
}
require(['cornerstone-core', 'cornerstoneTools', 'cornerstoneMath'], 
    function(cornerstone, cornerstoneTools, cornerstoneMath){
    cornerstoneTools.external.cornerstone = cornerstone
    cornerstoneTools.external.cornerstoneMath = cornerstoneMath
    $("#{item_name}").remove();
    console.log('Loading cs:'+cornerstone+','+cornerstoneMath+','+cornerstoneTools);
    element.append("<div id='{item_name}'></div>");
    
    $("#{item_name}").width("512px");
    $("#{item_name}").height("600px"); 
    var cs_viewer = document.getElementById('{item_name}');
    disableContextMenu(cs_viewer)
    cornerstone.enable(cs_viewer);
    cornerstoneTools.mouseInput.enable(cs_viewer);
    cornerstoneTools.mouseWheelInput.enable(cs_viewer);
    cornerstoneTools.wwwc.activate(cs_viewer, 1); // Left Click
    cornerstoneTools.pan.activate(cs_viewer, 2); // Middle Click
    cornerstoneTools.zoom.activate(cs_viewer, 4); // Right Click
})
"""
Javascript(fancy_format(simple_cornerstone_view, 
                                 item_name='dicom_viewer'))
ipw.HBox([
    size_scroller,
    ipw.VBox([noisy_img_but, half_img_but]),
    ipw.VBox([zoom_in_but, zoom_out_but])
])<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.14039716706013053, 'fraction_numerical': 0.018330787390640188, 'mean_word_length': 3.9430336307481126, 'pattern_counts': {'":': 0, '<': 14, '<?xml version=': 0, '>': 42, 'https://': 0, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '2', 'dir': 'jupyter-structured-clean-dedup', 'id': '197727', 'license': 'Apache-2.0', 'n_tokens_mistral': 2594, 'n_tokens_neox': 2462, 'n_words': 453, 'path': 'notebooks/view_cornerstone.ipynb', 'repo_name': 'betatim/cornerstone_widget', 'size': '7201'}