Openskin enhancements / fixes / proposals

Issue #810 resolved
Luuk created an issue

A colleague of mine (Wens Kong) worked on several enhancements concerning OpenSkin. I will put a list of the items here and try to cut the code she made into pieces and push it to the repository:

  • BUG: If patient is prone on the table, the phantom is positioned below the table
  • ENHANCEMENT: A head (smaller cylinder) is added to the phantom
  • ENHANCEMENT: The percentage of events (and DAP) that hits the phantom is calculated. This is an indication of the reliability of the skinmap
  • ENHANCEMENT: Summary results of OpenSkin are exported to a (new) model in OpenREM and shown on the rf_detail-page (related to #605, #595) → results are exported to Openrem, not yet on rf_detail_page, see #827
  • ENHANCEMENT: Made it possible to send an alert e-mail based on peak skin dose (related to #605, #595) → Done, see #827
  • ENHANCEMENT: (Still some work to do). Make it possible to calculate for skin dose for Philips systems. With an institute-dependent trick it is possible now. We need to do some extra work here to generalize this. For this it is necessary to add a model with a (per system) reference point definition (related to #595).

All items are implemented in our production environment today and we will see how stable it all runs.

Comments (23)

  1. Ed McDonagh

    This is great Luuk, and great to hear there are new contributers 🙂

    @Jonathan Cole , @David Platten - can you pitch in as to how we should manage these changes?

    Some obviously belong here as they relate to how openSkin relates to OpenREM in the interface and alerts etc. But other elements are about improving openSkin. Should these be directed to the openSkin repository and then merged in to this repository? Or merged here, then offered as PR from this repo to the openSkin ‘upstream’ repo?

  2. Jonathan Cole

    I’m delighted Luuk and Wens Kong are working on openSkin. I’m still unfortunately having to prioritise other things at the moment.

    @Ed McDonagh if we are doing it properly we should update upstream and then merge to OpenREM or else close upstream and fold openSkin into OpenREM. Nobody is really using openSkin standalone anyway so it makes little practical difference. I don’t have strong opinions either way. We should definitely try and do some proper validation though both on the existing code and the new enhancements.

  3. David Platten

    I’m carrying out a project to validate the skin dose maps as part of my Doctorate of Clinical Science. I plan to publish the results as peer-reviewed journal articles.

  4. Ed McDonagh

    Thanks Jon. David, Luuk, do you have an opinion on how we should proceed with the code?

    I have write access to the openSkin project, I don’t know who else dose?

    Personally, I’d like to re-work the openSkin code for PEP8 compliance and add testing, in addition to all the lovely improvements Luuk and others want to make.

    To move forward with the upstream openSkin project, I think we’d need to do the following (ideally):

    • Have multiple people with access to commit/branch/PR etc
    • Set up pipelines and testing
    • Sort out a versioning and release scheme
    • Release to PyPI so that we can consume rather than reimplementing the code in OpenREM

    I think this would all be easier within the OpenREM repo, and make it easier to use more of the study data directly from the database (ref #595).

    The main disadvantage of this would be to anyone attempting to make use of openSkin with data held in a different system. I have no idea how prevalent this is? And whether we could maintain a calling function that would enable it to be used in isolation with an API? The email on the US mailbase enquiring about it saw it as ‘the engine for OpenREM skin mapping’, so it might already be considered as such. Also, I think the main drivers for improving the openSkin capabilities are probably from the OpenREM community. But I don’t know how many people contact you Jon from outside!

    They are my thoughts - what do you all think?

  5. Jonathan Cole

    As far as I recall only two people I know of have used it outside OpenREM, and one of them is me.

    Whichever way bests encourages development is fine by me. It’s probably best in OpenREM. I’ve no idea what PEP8 is or how to set up pipelines…

  6. Luuk reporter

    For me it would be fine to have OpenSkin only available in OpenREM. We don’t use it outside OpenREM and I see a lot of advantages (as Ed also already mentions). But indeed the downside is that it won’t be available stand-alone and decoupling after some time will probably be hard (if there is a wish).

    As it seems there are only a few people using it stand-alone I would suggest to add openSkin to the OpenREM repo (actually to make it part of (dependent on) OpenREM).


    Hi All,

    I would just like to enquire as to what the current status of this is? We have 3 Philips Allura Clarity labs exporting RDSR to OpenREM, and would like to make use of the skin dose mapping.

    Are the enhancements mentioned at the top generally available yet?

    If it is testing and validation of something that already exists that is required, we are happy to help out where we can.

    Within our group, we are not Python programmers (yet), but may be able to tap into people with expertise from the wider department if need be for the development of this.

  8. Luuk reporter

    Hi Tim,

    Thanks for your offer in helping with this development. We are having Allura systems ourselves and we made some changes that makes it working for us (most of the time with some quick and dirty hacks). But in essence we know what to do to make it work.

    Looking at the list above our next thing is to resolve the orientation bug in OpenSkin. First work on this is done.

    After that the work Wens did for the Philips Allura systems could be next. To do this nicely would mean some further integration of OpenREM and OpenSkin. It would be great if you could test in an early stage to see if it also works on different installation sites (different countries).

    I’ll let you know when we are ready for testing this.


    Great - thanks for the prompt response. Do you have any rough timescales in mind for the work?

  10. wens

    adds cylindrical head to phantom, doesnt scale yet with patientsize, uses skin map version to distinguish head and headless skindose calculation pickle files that may already be present refs #810

    → <<cset ba568cb08dfc>>

  11. David Platten

    I’ve tried to simplify the this.draw function in rfSkinDoseMap3dObject.js. I’m pasting it here as I can’t commit it at the moment, and don’t want to lose the code:

         * Internal function to return a colour for a skin dose map patch given
         * the i, j location in the skin dose map array
        this.getNewColour = function(i, j) {
            var _this = this;
            var currentDose = _this.skinDoseMap[j * _this.phantomHeight + i];
            var scaledDose = currentDose - (_this.windowLevel - (_this.windowWidth / 2.0));
            if (scaledDose < 0) {scaledDose = 0;}
            if (scaledDose > _this.windowWidth) {scaledDose = _this.windowWidth;}
            return _this.colourScale(scaledDose / _this.windowWidth).rgb();
         * Internal function to draw the 3d skin dose map
        this.draw = function () {
            var _this = this;
            var newColour, i, j, k, l, m, n;
            k = l = m = n = 0;
            for (i = 0; i < _this.phantomHeight; i++) {
                for (j = 0; j < _this.phantomFlatWidth; j++) {
                    newColour = this.getNewColour(i, j);
          [k] = newColour[0];
          [k+1] = newColour[1];
          [k+2] = newColour[2];
          [k+3] = 0;
                    k += 4;
                for (j = _this.phantomFlatWidth; j < _this.phantomFlatWidth+_this.phantomCurvedEdgeWidth; j++) {
                    newColour = this.getNewColour(i, j);
          [l] = newColour[0];
          [l+1] = newColour[1];
          [l+2] = newColour[2];
          [l+3] = 0;
                    l += 4;
                for (j = _this.phantomFlatWidth+_this.phantomCurvedEdgeWidth; j < _this.phantomFlatWidth*2+_this.phantomCurvedEdgeWidth; j++) {
                    newColour = this.getNewColour(i, j);
          [m] = newColour[0];
          [m+1] = newColour[1];
          [m+2] = newColour[2];
          [m+3] = 0;
                    m += 4;
                for (j = _this.phantomFlatWidth*2+_this.phantomCurvedEdgeWidth; j < _this.phantomFlatWidth*2+_this.phantomCurvedEdgeWidth*2; j++) {
                    newColour = this.getNewColour(i, j);
          [n] = newColour[0];
          [n+1] = newColour[1];
          [n+2] = newColour[2];
          [n+3] = 0;
                    n += 4;
            dataTextureFront.needsUpdate = true;
            dataTextureBack.needsUpdate  = true;
            dataTextureLeft.needsUpdate  = true;
            dataTextureRight.needsUpdate = true;

  12. David Platten

    Switched sign on head 'lid' rotation so that the top of the head is opaque (and blue). Also using phantomHeadRadius to set the radius of this lid as it is clearer. Refs issue #810

    → <<cset 7b490a366585>>

  13. Log in to comment