{
  "id": 48045,
  "title": "Create N-by-N pictures with random crops - workaround for ImageDataGenerator bug",
  "url": "/competitions/dog-breed-identification/discussion/48045",
  "author_name": "Daniel Morvay",
  "post_date": "2018-01-22T21:28:20.778000",
  "votes": 1,
  "comment_count": 0,
  "views": 0,
  "content": "<p>Hi,</p>\n\n<p>maybe some of you have already tried the Keras' ImageDataGenerator function -  called preprocessing_function - with custom functions. Well, it does not work properly. According to the Keras docs it should be executed before any other image manipulation.</p>\n\n<p>*\"preprocessing_function: function that will be implied on each input. The function will run before any other modification on it. The function should take one argument: one image (Numpy tensor with rank 3), and should output a Numpy tensor with the same shape.\"*</p>\n\n<p>So, for instance, if you would like to create pictures having resolution N-by-N - to avoid pictures to be distorted (dogs with egg-shape head or with football-shape head) - you could add here a smart script. Unfortunately there is a (reported but still not fixed) bug in ImageDataGenerator, so when you call this function the picture is already shaped into the target resolution, so it is already N-by-N.</p>\n\n<p>I've created two scripts.\nThe 2nd one reads the dogs data from a folder and copy them into train and validation class-folders, you can use to feed the datagen.flow_from_directory() function. \nThe 1st one creates N-by-N pictures using random \"best fit\" cropping.</p>\n\n<p>Here they are:</p>\n\n<pre><code>def preproc_img_2square(img):\npic = image.img_to_array(img)\nif K.image_data_format() == 'channels_last':\n    sizex, sizey, ch = np.shape(pic)\nelse:\n    ch, sizex, sizey = np.shape(pic) \n\nif sizex == sizey:\n    return img\nif sizex &gt; sizey: \n    start_random_crop_from = random.randint(0,sizex-sizey)\n    pic = pic[start_random_crop_from:start_random_crop_from+sizey,:]\n    return image.array_to_img(pic)\nelse: \n    start_random_crop_from = random.randint(0,sizey-sizex)\n    pic =  pic[:,start_random_crop_from:start_random_crop_from+sizex]\n    return image.array_to_img(pic)\n</code></pre>\n\n<p>Let's copy the files into class folders:</p>\n\n<pre><code>trainlabels = np.loadtxt(workdir + \"\\\\labels.csv\", skiprows=1, dtype='unicode', delimiter=',')\n\ngonogo = input('Do you want to build up the folder=classlabel based folder structure? [Y]/[N]')\n\nif gonogo=='Y':\nsplitratio = input('What is the split ratio of train/valid? (90% = 0.9)')\nsplitatnumber=int(np.floor(np.size(trainlabels[:,0])*float(splitratio)))\nimport os\nfor label in trainlabels[0:splitatnumber]:\n    label0 = label[0][2:].replace(\"'\", \"\")\n    label1 = label[1][2:].replace(\"'\", \"\")\n    sourceuri = origindir + '\\\\train\\\\' + label0 +'.jpg'\n    desturi = classfolderdirtrain + '\\\\' + label1 + '\\\\' + label0 +'.jpg' \n    if not os.path.exists(classfolderdirtrain + '\\\\' + label1):\n        os.makedirs(classfolderdirtrain + '\\\\' + label1)\n    img = image.load_img(sourceuri)\n    img2 = preproc_img_2square(img)\n    img2.save(desturi)\n    print(label0)\n\nfor label in trainlabels[splitatnumber:]:\n    label0 = label[0][2:].replace(\"'\", \"\")\n    label1 = label[1][2:].replace(\"'\", \"\")\n    sourceuri = origindir + '\\\\train\\\\' + label0 +'.jpg'\n    desturi = classfolderdirvalidation + '\\\\' + label1 + '\\\\' + label0 +'.jpg' \n    if not os.path.exists(classfolderdirvalidation + '\\\\' + label1):\n        os.makedirs(classfolderdirvalidation + '\\\\' + label1)\n    img = image.load_img(sourceuri)\n    img2 = preproc_img_2square(img)\n    img2.save(desturi)\n    print(label0)\n</code></pre>\n\n<p>Let me know if it helped...</p>",
  "messages": [
    {
      "id": 272346,
      "postDate": "2018-01-22T21:28:20.777Z",
      "content": "<p>Hi,</p>\n\n<p>maybe some of you have already tried the Keras' ImageDataGenerator function -  called preprocessing_function - with custom functions. Well, it does not work properly. According to the Keras docs it should be executed before any other image manipulation.</p>\n\n<p>*\"preprocessing_function: function that will be implied on each input. The function will run before any other modification on it. The function should take one argument: one image (Numpy tensor with rank 3), and should output a Numpy tensor with the same shape.\"*</p>\n\n<p>So, for instance, if you would like to create pictures having resolution N-by-N - to avoid pictures to be distorted (dogs with egg-shape head or with football-shape head) - you could add here a smart script. Unfortunately there is a (reported but still not fixed) bug in ImageDataGenerator, so when you call this function the picture is already shaped into the target resolution, so it is already N-by-N.</p>\n\n<p>I've created two scripts.\nThe 2nd one reads the dogs data from a folder and copy them into train and validation class-folders, you can use to feed the datagen.flow_from_directory() function. \nThe 1st one creates N-by-N pictures using random \"best fit\" cropping.</p>\n\n<p>Here they are:</p>\n\n<pre><code>def preproc_img_2square(img):\npic = image.img_to_array(img)\nif K.image_data_format() == 'channels_last':\n    sizex, sizey, ch = np.shape(pic)\nelse:\n    ch, sizex, sizey = np.shape(pic) \n\nif sizex == sizey:\n    return img\nif sizex &gt; sizey: \n    start_random_crop_from = random.randint(0,sizex-sizey)\n    pic = pic[start_random_crop_from:start_random_crop_from+sizey,:]\n    return image.array_to_img(pic)\nelse: \n    start_random_crop_from = random.randint(0,sizey-sizex)\n    pic =  pic[:,start_random_crop_from:start_random_crop_from+sizex]\n    return image.array_to_img(pic)\n</code></pre>\n\n<p>Let's copy the files into class folders:</p>\n\n<pre><code>trainlabels = np.loadtxt(workdir + \"\\\\labels.csv\", skiprows=1, dtype='unicode', delimiter=',')\n\ngonogo = input('Do you want to build up the folder=classlabel based folder structure? [Y]/[N]')\n\nif gonogo=='Y':\nsplitratio = input('What is the split ratio of train/valid? (90% = 0.9)')\nsplitatnumber=int(np.floor(np.size(trainlabels[:,0])*float(splitratio)))\nimport os\nfor label in trainlabels[0:splitatnumber]:\n    label0 = label[0][2:].replace(\"'\", \"\")\n    label1 = label[1][2:].replace(\"'\", \"\")\n    sourceuri = origindir + '\\\\train\\\\' + label0 +'.jpg'\n    desturi = classfolderdirtrain + '\\\\' + label1 + '\\\\' + label0 +'.jpg' \n    if not os.path.exists(classfolderdirtrain + '\\\\' + label1):\n        os.makedirs(classfolderdirtrain + '\\\\' + label1)\n    img = image.load_img(sourceuri)\n    img2 = preproc_img_2square(img)\n    img2.save(desturi)\n    print(label0)\n\nfor label in trainlabels[splitatnumber:]:\n    label0 = label[0][2:].replace(\"'\", \"\")\n    label1 = label[1][2:].replace(\"'\", \"\")\n    sourceuri = origindir + '\\\\train\\\\' + label0 +'.jpg'\n    desturi = classfolderdirvalidation + '\\\\' + label1 + '\\\\' + label0 +'.jpg' \n    if not os.path.exists(classfolderdirvalidation + '\\\\' + label1):\n        os.makedirs(classfolderdirvalidation + '\\\\' + label1)\n    img = image.load_img(sourceuri)\n    img2 = preproc_img_2square(img)\n    img2.save(desturi)\n    print(label0)\n</code></pre>\n\n<p>Let me know if it helped...</p>",
      "rawMarkdown": "Hi,\n\nmaybe some of you have already tried the Keras' ImageDataGenerator function -  called preprocessing_function - with custom functions. Well, it does not work properly. According to the Keras docs it should be executed before any other image manipulation.\n\n*\"preprocessing_function: function that will be implied on each input. The function will run before any other modification on it. The function should take one argument: one image (Numpy tensor with rank 3), and should output a Numpy tensor with the same shape.\"*\n\nSo, for instance, if you would like to create pictures having resolution N-by-N - to avoid pictures to be distorted (dogs with egg-shape head or with football-shape head) - you could add here a smart script. Unfortunately there is a (reported but still not fixed) bug in ImageDataGenerator, so when you call this function the picture is already shaped into the target resolution, so it is already N-by-N.\n\nI've created two scripts.\nThe 2nd one reads the dogs data from a folder and copy them into train and validation class-folders, you can use to feed the datagen.flow_from_directory() function. \nThe 1st one creates N-by-N pictures using random \"best fit\" cropping.\n\nHere they are:\n\n    def preproc_img_2square(img):\n    pic = image.img_to_array(img)\n    if K.image_data_format() == 'channels_last':\n        sizex, sizey, ch = np.shape(pic)\n    else:\n        ch, sizex, sizey = np.shape(pic) \n    \n    if sizex == sizey:\n        return img\n    if sizex &gt; sizey: \n        start_random_crop_from = random.randint(0,sizex-sizey)\n        pic = pic[start_random_crop_from:start_random_crop_from+sizey,:]\n        return image.array_to_img(pic)\n    else: \n        start_random_crop_from = random.randint(0,sizey-sizex)\n        pic =  pic[:,start_random_crop_from:start_random_crop_from+sizex]\n        return image.array_to_img(pic)\n\n    \nLet's copy the files into class folders:\n\n    trainlabels = np.loadtxt(workdir + \"\\\\labels.csv\", skiprows=1, dtype='unicode', delimiter=',')\n\n    gonogo = input('Do you want to build up the folder=classlabel based folder structure? [Y]/[N]')\n\n    if gonogo=='Y':\n    splitratio = input('What is the split ratio of train/valid? (90% = 0.9)')\n    splitatnumber=int(np.floor(np.size(trainlabels[:,0])*float(splitratio)))\n    import os\n    for label in trainlabels[0:splitatnumber]:\n        label0 = label[0][2:].replace(\"'\", \"\")\n        label1 = label[1][2:].replace(\"'\", \"\")\n        sourceuri = origindir + '\\\\train\\\\' + label0 +'.jpg'\n        desturi = classfolderdirtrain + '\\\\' + label1 + '\\\\' + label0 +'.jpg' \n        if not os.path.exists(classfolderdirtrain + '\\\\' + label1):\n            os.makedirs(classfolderdirtrain + '\\\\' + label1)\n        img = image.load_img(sourceuri)\n        img2 = preproc_img_2square(img)\n        img2.save(desturi)\n        print(label0)\n        \n    for label in trainlabels[splitatnumber:]:\n        label0 = label[0][2:].replace(\"'\", \"\")\n        label1 = label[1][2:].replace(\"'\", \"\")\n        sourceuri = origindir + '\\\\train\\\\' + label0 +'.jpg'\n        desturi = classfolderdirvalidation + '\\\\' + label1 + '\\\\' + label0 +'.jpg' \n        if not os.path.exists(classfolderdirvalidation + '\\\\' + label1):\n            os.makedirs(classfolderdirvalidation + '\\\\' + label1)\n        img = image.load_img(sourceuri)\n        img2 = preproc_img_2square(img)\n        img2.save(desturi)\n        print(label0)\n\nLet me know if it helped...\n\n",
      "votes": 1
    }
  ],
  "comments": [],
  "raw_markdown_by_id": {
    "272346": "Hi,\n\nmaybe some of you have already tried the Keras' ImageDataGenerator function -  called preprocessing_function - with custom functions. Well, it does not work properly. According to the Keras docs it should be executed before any other image manipulation.\n\n*\"preprocessing_function: function that will be implied on each input. The function will run before any other modification on it. The function should take one argument: one image (Numpy tensor with rank 3), and should output a Numpy tensor with the same shape.\"*\n\nSo, for instance, if you would like to create pictures having resolution N-by-N - to avoid pictures to be distorted (dogs with egg-shape head or with football-shape head) - you could add here a smart script. Unfortunately there is a (reported but still not fixed) bug in ImageDataGenerator, so when you call this function the picture is already shaped into the target resolution, so it is already N-by-N.\n\nI've created two scripts.\nThe 2nd one reads the dogs data from a folder and copy them into train and validation class-folders, you can use to feed the datagen.flow_from_directory() function. \nThe 1st one creates N-by-N pictures using random \"best fit\" cropping.\n\nHere they are:\n\n    def preproc_img_2square(img):\n    pic = image.img_to_array(img)\n    if K.image_data_format() == 'channels_last':\n        sizex, sizey, ch = np.shape(pic)\n    else:\n        ch, sizex, sizey = np.shape(pic) \n    \n    if sizex == sizey:\n        return img\n    if sizex &gt; sizey: \n        start_random_crop_from = random.randint(0,sizex-sizey)\n        pic = pic[start_random_crop_from:start_random_crop_from+sizey,:]\n        return image.array_to_img(pic)\n    else: \n        start_random_crop_from = random.randint(0,sizey-sizex)\n        pic =  pic[:,start_random_crop_from:start_random_crop_from+sizex]\n        return image.array_to_img(pic)\n\n    \nLet's copy the files into class folders:\n\n    trainlabels = np.loadtxt(workdir + \"\\\\labels.csv\", skiprows=1, dtype='unicode', delimiter=',')\n\n    gonogo = input('Do you want to build up the folder=classlabel based folder structure? [Y]/[N]')\n\n    if gonogo=='Y':\n    splitratio = input('What is the split ratio of train/valid? (90% = 0.9)')\n    splitatnumber=int(np.floor(np.size(trainlabels[:,0])*float(splitratio)))\n    import os\n    for label in trainlabels[0:splitatnumber]:\n        label0 = label[0][2:].replace(\"'\", \"\")\n        label1 = label[1][2:].replace(\"'\", \"\")\n        sourceuri = origindir + '\\\\train\\\\' + label0 +'.jpg'\n        desturi = classfolderdirtrain + '\\\\' + label1 + '\\\\' + label0 +'.jpg' \n        if not os.path.exists(classfolderdirtrain + '\\\\' + label1):\n            os.makedirs(classfolderdirtrain + '\\\\' + label1)\n        img = image.load_img(sourceuri)\n        img2 = preproc_img_2square(img)\n        img2.save(desturi)\n        print(label0)\n        \n    for label in trainlabels[splitatnumber:]:\n        label0 = label[0][2:].replace(\"'\", \"\")\n        label1 = label[1][2:].replace(\"'\", \"\")\n        sourceuri = origindir + '\\\\train\\\\' + label0 +'.jpg'\n        desturi = classfolderdirvalidation + '\\\\' + label1 + '\\\\' + label0 +'.jpg' \n        if not os.path.exists(classfolderdirvalidation + '\\\\' + label1):\n            os.makedirs(classfolderdirvalidation + '\\\\' + label1)\n        img = image.load_img(sourceuri)\n        img2 = preproc_img_2square(img)\n        img2.save(desturi)\n        print(label0)\n\nLet me know if it helped...\n\n"
  }
}