{"id":20630631,"url":"https://github.com/brookisme/imagebox","last_synced_at":"2025-04-15T18:22:44.027Z","repository":{"id":62570483,"uuid":"195498928","full_name":"brookisme/imagebox","owner":"brookisme","description":"Image Kit: utilities for reading/writing/augmenting images","archived":false,"fork":false,"pushed_at":"2022-08-27T13:45:26.000Z","size":84,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-07T07:44:49.667Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/brookisme.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-07-06T04:52:33.000Z","updated_at":"2024-11-25T18:38:17.000Z","dependencies_parsed_at":"2022-11-03T17:15:33.945Z","dependency_job_id":null,"html_url":"https://github.com/brookisme/imagebox","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brookisme%2Fimagebox","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brookisme%2Fimagebox/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brookisme%2Fimagebox/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brookisme%2Fimagebox/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/brookisme","download_url":"https://codeload.github.com/brookisme/imagebox/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248631722,"owners_count":21136560,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-16T14:09:04.496Z","updated_at":"2025-04-15T18:22:44.008Z","avatar_url":"https://github.com/brookisme.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"### ImageBox: python utilities for working with multispectral imagery \n\nImageBox contains four main modules:\n\n- [io](#io): a rasterio wrapper for reading/writing imagery. Simplifies reading windows by returning a window specific profile\n- [processor](#processor): a number of methods for processing images such as normalization, mapping categorical values, augmentation, etc.\n- [indices](#indices): simplifies computing band-indices. includes a number of preset band indices, such as NDVI, NDWI, BuiltUp-Index.\n- [handler](#handler): A class that handles processing for target and input data simultaneously. This is particularly useful in machine-learning. The class simplifies the creation of (pytorch) Datasets/Dataloaders or (keras) data-generators.\n \n\n---\n\n\u003ca name='install'\u003e\u003c/a\u003e\n##### INSTALL\n\n```bash\ngit clone https://github.com/brookisme/imagebox.git\ncd imagebox\npip install -e .\n```\n\n---\n\n\u003ca name='io'\u003e\u003c/a\u003e\n##### IO\n\n_a rasterio wrapper for reading/writing imagery_\n\nThis module contains two simple methods:\n\n##### io.read(path,window=None,window_profile=True,dtype=None)\n\n```python\nArgs: \n    - path\u003cstr\u003e: source path\n    - window\u003ctuple|Window\u003e: col_off, row_off, width, height\n    - window_profile\u003cbool\u003e:\n        - if True return profile for the window data\n        - else return profile for the src-image\n    - dtype\u003cstr\u003e:\nReturns:\n    \u003ctuple\u003e np.array, image-profile\n```\n\n##### io.write(im,path,profile,makedirs=True) \n\n```python\n    Args: \n        - im\u003cnp.array\u003e: image\n        - path\u003cstr\u003e: destination path\n        - profile\u003cdict\u003e: image profile\n        - makedirs\u003cbool\u003e: if True create necessary directories\n```\n\n\n---\n\n\u003ca name='processor'\u003e\u003c/a\u003e\n##### Processor\n\nThis module contains a number of methods for processing images. See doc-strings for details. Here current list of methods:\n\n- center: center image around mean\n- normalize: normalize image\n- denormalize: turn a normalized image into an RGB \"denormalized\" image\n- map_values: map categorical pixel values to new values\n- to_categorical: turn categorical image into a categorical (binary-multi-band) image\n- crop: crop image\n- augmentation: returns a random flip and/or rotation value to be used when augmenting data\n- augment: augment data with flips and/or 90-degree rotations\n\n\n---\n\n\u003ca name='indices'\u003e\u003c/a\u003e\n##### Indices\n\nThis module allows you to compute combination of band values to create band indices.  There are a number of pre-configured band combinations as well as methods for **normalized difference** band combinations, **linear combinations** of bands and **ratios of linear combinations** of bands.\n\nIMPORTANT NOTE: band index definitions in `INDICES`  are based on band ordering red, green, blue, nir, red-edge, swir1.  If using different bands/band-ordering you can use `INDICES` as a guide to how one constructs band-indices.\n\nHere is a list of pre-configured indices:\n\n- ndvi\n- ndwi\n- ndwi_leaves\n- ndbi\n- built_up\n- greeness\n- chlogreen\n- gcvi\n- evi_modis\n- evi_s2\n\nexamples:\n\n```python\n# NDVI from predefined structure\nndvi=indices.index(im,'ndvi')\n\n# NDVI from normalized_difference method:\nndvi2=indices.normalized_difference(im,3,0)\n```\n\nThose are the simplest examples, but you should be able to create almost any combination of the bands using this module. See doc-strings for details. Here current list of methods:\n\n- index: handles pre-configured indices and is a wrapper method for all methods below\n- normalized_difference: for bands b1,b2 computes `(b1-b2)/(b1+b2)`\n- linear_combo: for bands b1,...bN and constant C computes `b1+b2+...+bN + C`\n- ratio_index: for bands n1,...,nN and d1,...,dM and constants C, Cn, Cd computes `((n1+n2+...+nN + Cn)/(d1+d2+...+dN + Cd))+C`\n\n---\n\n\u003ca name='handler'\u003e\u003c/a\u003e\n##### Handler\n\n_handlers processing for target and input data_\n\nThis module contains two classes:\n\n- [InputTargetHandler](#inpttarg): Processes input and target data in conjunction\n- [Tiller](#tiller): for a given boundary shape, this generates windows of a given size and overlap which can be used to tile an image\n\n\u003ca name='inpttarg'\u003e\n\n###### InputTargetHandler\n\nThe InputTargetHandler is able to:\n\n- compute band indices (ndvi, ndwi, ...)\n- normalize or center the input imagery\n- augment data (flip/90-deg-rotation)\n- map the values in the target imagery to new values\n- convert the target to a categorical (binary-multi-band) image\n- select bands\n- crop input and/or target data\n- tile the input/target into a grid of images (i.e. a single 900x900 image can be treated as 9 300x300 images)\n- (float_cropping) for a window size smaller than the image (or image tile) randomly selecting a window at a arbitrary point within the the image (or image tile)\n\nAn example speaks some number of words:\n\nThe example below creates a pytorch Dataloader/Dataset from a dataframe with rows-containing input and target filenames. The bulk of the code is simply getting and returning those input and target filenames. All the manipulation is done by InputTargetHandler:\n\n```python\nclass UrbanLandUseDS(Dataset):\n    @staticmethod\n    def load_dataframe(dataframe):\n        if isinstance(dataframe,str):\n            dataframe=pd.read_csv(dataframe)\n        return dataframe\n\n\n    @classmethod\n    def loader(cls,\n            dataframe,\n            batch_size=DEFAULT_BATCH_SIZE,\n            partial_batches=False,\n            loader_kwargs={},\n            **kwargs):\n        r\"\"\" convenience method for loading the DataLoader directly.\n            \n            Args:\n                see class args\n\n            Returns:\n                dataloader \n        \"\"\"\n\n\n        return DataLoader(cls(dataframe,**kwargs),batch_size=batch_size,**loader_kwargs)\n\n\n\n    def __init__(self,\n            dataframe,\n            data_dir=DATA,\n            resolution=RESOLUTION,\n            input_bands=None,\n            means=None,\n            stdevs=None,\n            band_indices=None,\n            value_map=VALUE_MAP,\n            default_mapped_value=NB_CATEGORIES,\n            to_categorical=False,\n            nb_categories=NB_CATEGORIES,\n            augment=True,\n            cropping=None,\n            float_cropping=None,\n            input_dtype=INPUT_DTYPE,\n            target_dtype=TARGET_DTYPE,\n            randomize=True,\n            train_mode=True):\n        self.randomize=randomize\n        self._set_data(dataframe)\n        self.train_mode=train_mode\n        self.root_dir=f'{data_dir}/{resolution}'\n        self.handler=InputTargetHandler(\n            input_bands=input_bands,\n            means=means,\n            stdevs=stdevs,\n            band_indices=band_indices,\n            value_map=value_map,\n            default_mapped_value=default_mapped_value,\n            to_categorical=to_categorical,\n            nb_categories=nb_categories,\n            augment=augment,\n            cropping=cropping,\n            float_cropping=float_cropping,\n            input_dtype=input_dtype,\n            target_dtype=target_dtype)\n\n\n    def __len__(self):\n        return len(self.aoi_names)\n\n\n    def __getitem__(self, index):\n        self.select_data(index)\n        self.handler.set_float_window()\n        self.handler.set_augmentation()\n        inpt,inpt_p=self.handler.input(self.input_path,return_profile=True)\n        targ,targ_p=self.handler.target(self.target_path,return_profile=True)\n        inpt_p=self._clean(inpt_p)\n        targ_p=self._clean(targ_p)\n        if self.train_mode:\n            itm={\n                'input': inpt, \n                'target': targ }\n        else:\n            itm={\n                'input': inpt, \n                'target': targ,\n                'index': self.index,\n                'aoi_name': self.aoi_name,\n                'input_path': self.input_path,\n                'target_path': self.target_path,\n                'float_x': self.handler.float_x,\n                'float_y': self.handler.float_y,\n                'k': self.handler.k,\n                'flip': self.handler.flip,\n                'input_profile': inpt_p,\n                'target_profile': targ_p }\n        return itm\n            \n\n    def select_data(self,index):\n        \"\"\" select data for index without loading/processing images\n        \"\"\"\n        self.index=index\n        self.aoi_name=self.aoi_names[index]\n        self.row=self.dataframe[self.dataframe.aoi_name==self.aoi_name].sample().iloc[0]\n        self.target_path=f'{self.root_dir}/target/{self.row.region}/{self.row.target_file}'\n        self.input_path=f'{self.root_dir}/input/{self.row.region}/{self.row.input_file}'\n\n\n    def reset(self,limit=None):\n        \"\"\" reset the generator\n            * if randomize: shuffle aoi\n            * if limit: limit aois, reset size\n        \"\"\"\n        if self.randomize:\n            shuffle(self.aoi_names)\n        if limit:\n            self.aoi_names=self.aoi_names[:limit]\n            self.size=len(self.aoi_names)\n\n    #\n    # INTERNAL\n    #\n    def _set_data(self,dataframe):\n        self.dataframe=UrbanLandUseDS.load_dataframe(dataframe)\n        self.aoi_names=list(self.dataframe.aoi_name.unique())\n        self.size=len(self.aoi_names)\n        self.reset()\n        \n\n    def _clean(self,obj):\n        return  { k:v for k,v in obj.items() if v is not None }\n\n```\n\n\n\u003ca name='tiller'\u003e\n    \n###### Tiller\n\nFor a given boundary shape generate windows (x-offset, y-offset, width, height) of a given size and overlap\n\n```\nUsage:\n    im=np.arange(1024**2).reshape((1024,1024))\n    tiller=hand.Tiller(boundary_shape=im.shape,size=100,overlap=10)\n    xoff,yoff,width,height=tiller[0]\n    print(\"NB WINDOWS:\",len(tiller))\n    print(\"WINDOW-0:\",xoff,yoff,width,height)\n    im[yoff:yoff+height,xoff:xoff+width]\n    ### output:\n    NB WINDOWS: 115600\n    WINDOW-0: 1 1 5 5\n    array([[1025, 1026, 1027, 1028, 1029],\n           [2049, 2050, 2051, 2052, 2053],\n           [3073, 3074, 3075, 3076, 3077],\n           [4097, 4098, 4099, 4100, 4101],\n           [5121, 5122, 5123, 5124, 5125]])\nArgs:\n    boundary_width/height\u003cint|None\u003e: \n        - width/height of boundary\n        - required if boundary shape not specified\n    boundary_shape\u003ctuple|None\u003e:\n        - shape tuple\n        - required if width/height not specified\n    size\u003cint\u003e: tile size (width/height - only supports square tiles)\n    overlap\u003cint\u003e: overlap between tiles\n```\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrookisme%2Fimagebox","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbrookisme%2Fimagebox","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrookisme%2Fimagebox/lists"}