Chained callback with an image

Hello all,

I am new to Dash. I am trying to create an app where I input a brain MRI image. Then, I would use a previously built CNN model to predict whether the imported image has a brain tumor in it or not.

I am struggling in managing to use the image imported in the first callback as the input from which to predict with the model. Here is my code so far:

import datetime
import dash
from dash.dependencies import Input, Output, State
import dash_core_components as dcc
import dash_html_components as html

external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']

app = dash.Dash(__name__, external_stylesheets=external_stylesheets)

app.layout = html.Div([
    dcc.Upload(
        id='upload-image',
        children=html.Div([
            'Drag and Drop or ',
            html.A('Select Files')
        ]),
        style={
            'width': '100%',
            'height': '60px',
            'lineHeight': '60px',
            'borderWidth': '1px',
            'borderStyle': 'dashed',
            'borderRadius': '5px',
            'textAlign': 'center',
            'margin': '10px'
        },
        # Allow multiple files to be uploaded
        multiple=False
    ),
    html.Div(id='output-image-upload'),
    
    html.Div(id='prediction', style={'position':'absolute', 'left':'950px', 'top':'400px'}),

])

def parse_contents(contents, filename, date):
    return html.Div([
        html.H5(filename),
        html.H6(datetime.datetime.fromtimestamp(date)),

        # HTML images accept base64 encoded strings in the same format
        # that is supplied by the upload
        html.Img(src=contents),
        html.Hr(),
        html.Div('Raw Content'),
        html.Pre(contents[0:200] + '...', style={
            'whiteSpace': 'pre-wrap',
            'wordBreak': 'break-all'
        })
    ])


@app.callback(Output('output-image-upload', 'children'),
              [Input('upload-image', 'contents')],
              [State('upload-image', 'filename'),
               State('upload-image', 'last_modified')])
def update_output(list_of_contents, list_of_names, list_of_dates):        
    
    if list_of_contents is not None:
        children = [
            parse_contents(c, n, d) for c, n, d in
            zip(list_of_contents, list_of_names, list_of_dates)]
    
        return children
   
 
@app.callback(Output('prediction', 'children'),
              [Input('output-image-upload', 'children')])

def update_output(output_before):        
    
    image = output_before
    
    model = load_model('model.h5') 
    dim = (150, 150)
    img = Image.open(image)
    x = np.array(img.resize(dim))
    x = x.reshape(1,150,150,3)
    answ = model.predict_on_batch(x)

    classification = np.where(answ == np.amax(answ))[1][0]
    pred=str(answ[0][classification]*100) + '% Confidence This Is ' + names(classification)
    
    return pred    
    
    
if __name__ == '__main__':
    app.run_server(debug=True, use_reloader=False)

The problem really comes from the second callback where I try to use the image from the previous callback as an input. The rest works great.

Any ideas? Should you have any questions, let me know!

Thanks a lot in advance!

Hi @francois_st-amant welcome to the forum! The output-image-upload component does not know much about its children, in the second callback you need to have the image component as input, hence you need to give it an id in the first callback (and disable callback exceptions as explained in the Dash callback doc). Another solution would be to define the different elements created in parse_contents in the layout of your app, and have them as Outputs of the first callback (multiple outputs are possible). If you have potentially several images uploaded at the same time, you can be interested in pattern-matching callbacks.

Hello Emmanuelle, thanks for your reply!

How do I go about giving an id to the image component in the first callback? Do I need to create a second output for that callback? Will I also need another html.Div for this new output?

Sorry if my questions seem trivial! Thanks a lot!