1

I would like to create a FastAPI application for my image classification model. But when I upload the image file, it can not make predictions, because my model is setting the image as (256,256), but the image that gets uploaded is not (256,256). How can I resize the image after uploading and before getting predictions?

from fastapi import FastAPI, File, UploadFile
from fastapi.middleware.cors import CORSMiddleware
import uvicorn
import numpy as np
from io import BytesIO
from PIL import Image
import tensorflow as tf

app = FastAPI()

MODEL = tf.keras.models.load_model("D:/Works/M.6/New Soil/Classification/Model/SoilModel.h5")

CLASS_NAMES = ["Clay", "Loam", "Red","Sandy"]

@app.get("/home")
async def ping():
    return "Hello, Welcome to CropChat"

def read_file_as_image(data) -> np.ndarray:
    image = np.array(Image.open(BytesIO(data)))
    return image

@app.post("/predict")
async def predict(
    file: UploadFile = File(...)
):
    image = read_file_as_image(await file.read())
    img_batch = np.expand_dims(image, 0)
    
    predictions = MODEL.predict(img_batch)

    predicted_class = CLASS_NAMES[np.argmax(predictions[0])]
    confidence = np.max(predictions[0])
    return {
        'class': predicted_class,
        'confidence': float(confidence)
    }

if __name__ == "__main__":
    uvicorn.run(app, host='localhost', port=8000)

I tried to use image.resize, but it doesn't work.

If you guys have some recommendations for my code please let me know. Thank you.

Chris
  • 18,724
  • 6
  • 46
  • 80
PhidPhew
  • 21
  • 2

2 Answers2

0

Not sure which image object you have used .resize(dim: tuple) but dropping the correct way to do, it should be working. actually, you have to resize before converting to an array.

def read_file_as_image(data) -> np.ndarray:
    size = (256, 256)
    image = np.array(Image.open(BytesIO(data)).resize(size))
    return image
umair mehmood
  • 529
  • 2
  • 5
  • 17
0

The following answer is heavily based on this answer and this answer.

Note that using a BytesIO stream, as shown in your question, would not be necessary. You could instead pass the actual file object to Image.open(), using the .file attribute of UploadFile (see the linked answers above for more details). In that case, please make sure to define the endpoint with normal def instead of async def (see this answer for more details on that topic). If you prefer using async def, creating a new BytesIO() stream after reading the file bytes using await file.read() would also be feasible. Another possible option would be to use Image.frombytes(), similar to this PIL.Image.frombytes(), however, without creating a new image object.

To resize the image, you could use Image.resize():

Image.resize(size, resample=None, box=None, reducing_gap=None)

Returns a resized copy of this image.

PARAMETERS:

  • size – The requested size in pixels, as a 2-tuple: (width, height).
  • ...

Example

from fastapi import FastAPI, UploadFile, File, HTTPException
from PIL import Image
import numpy as np

app = FastAPI()


@app.post("/upload")
def upload(file: UploadFile = File()):
    try:        
        im = Image.open(file.file)
        
        # resize and save Image
        im = im.resize((256, 256))
        im.save('out.png')
        
        # convert Image to array
        arr = np.asarray(im)
        
        return 'OK'
    except Exception:
        raise HTTPException(status_code=500, detail='Something went wrong')
    finally:
        file.file.close()
        im.close()

Note

If you would like to preserve the aspect ratio of the image, instead of using im.resize(), you could use Image.thumbnail() (an example is also given in the documentation here). However, note that using this function might not result in a 256 x 256 image, as you requested (it might instead result in a 256 x 160, for instance, regardless of the size tuple passed to the function).

Image.thumbnail(size, resample=Resampling.BICUBIC, reducing_gap=2.0)

Make this image into a thumbnail. This method modifies the image to contain a thumbnail version of itself, no larger than the given size. This method calculates an appropriate thumbnail size to preserve the aspect of the image, calls the draft() method to configure the file reader (where applicable), and finally resizes the image.

Note that this function modifies the Image object in place. If you need to use the full resolution image as well, apply this method to a copy() of the original image.

PARAMETERS:

  • size – The requested size in pixels, as a 2-tuple: (width, height).
  • resample – Optional resampling filter. This can be one of Resampling.NEAREST, Resampling.BOX, Resampling.BILINEAR, Resampling.HAMMING, Resampling.BICUBIC or Resampling.LANCZOS. If omitted, it defaults to Resampling.BICUBIC. (was Resampling.NEAREST prior to version 2.5.0). See: Filters
  • ...

Example

im.thumbnail((256, 256), Image.LANCZOS)
im.save('out.png')
Chris
  • 18,724
  • 6
  • 46
  • 80