{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":112509,"databundleVersionId":14254895,"sourceType":"competition"},{"sourceId":13886785,"sourceType":"datasetVersion","datasetId":8818687}],"dockerImageVersionId":31192,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# Environment\nimport os, sys, subprocess, importlib\nos.environ['CUDA_VISIBLE_DEVICES'] = ''\nos.environ.setdefault('KERAS_BACKEND', 'tensorflow')\n\nimport tensorflow as tf\nimport keras\nimport numpy as np, pandas as pd, cv2\nimport sklearn, google.protobuf as protobuf, grpc\nprint('Python', sys.version.split()[0], 'TF', tf.__version__, 'Keras', getattr(keras, '__version__', ''))\nprint('NumPy', np.__version__, 'Pandas', pd.__version__, 'OpenCV', cv2.__version__)\nprint('GPUs visible:', tf.config.list_physical_devices('GPU'))\n\nexpected_prefix = {\n    'tensorflow': '2.18',\n    'keras': '3.8',\n    'pandas': '2.',\n    'scikit-learn': '1.',\n    'opencv-python': '4.',\n    'protobuf': '4.',\n    'grpcio': '1.'\n}\nversions = {\n    'tensorflow': tf.__version__,\n    'keras': getattr(keras, '__version__', ''),\n    'pandas': pd.__version__,\n    'scikit-learn': sklearn.__version__,\n    'opencv-python': cv2.__version__,\n    'protobuf': getattr(protobuf, '__version__', getattr(protobuf, '__version__', '')),\n    'grpcio': grpc.__version__\n}\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-26T23:13:29.117216Z","iopub.execute_input":"2025-11-26T23:13:29.118064Z","iopub.status.idle":"2025-11-26T23:13:29.144166Z","shell.execute_reply.started":"2025-11-26T23:13:29.118036Z","shell.execute_reply":"2025-11-26T23:13:29.142865Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Load scaler metadata\nimport pickle\nwith open('/kaggle/input/jp-csiro-v3/scaler_metadata.pkl','rb') as f: meta = pickle.load(f)\nscaler = meta['scaler']\ntarget_columns = meta['target_columns']\nprimary_targets = meta['primary_targets']\nprint('Loaded scaler metadata. Targets:', len(target_columns))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-26T23:14:17.677767Z","iopub.execute_input":"2025-11-26T23:14:17.678102Z","iopub.status.idle":"2025-11-26T23:14:17.728288Z","shell.execute_reply.started":"2025-11-26T23:14:17.678076Z","shell.execute_reply":"2025-11-26T23:14:17.727455Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Load model (prefer Keras 3 loader, then tf.keras)\nimport keras, tensorflow as tf, os\n\nmodel = keras.models.load_model(\"/kaggle/input/jp-csiro-v3/biomass_multicnn_lr_0.001.keras\")\n          \nprint('Model ready. Name:', model.name)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-26T23:18:14.518546Z","iopub.execute_input":"2025-11-26T23:18:14.518896Z","iopub.status.idle":"2025-11-26T23:18:14.966466Z","shell.execute_reply.started":"2025-11-26T23:18:14.518873Z","shell.execute_reply":"2025-11-26T23:18:14.965595Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"try:\n    optimized_color_ranges = json.load(open('/kaggle/input/jp-csiro-v3/optimal_color_ranges.json'))['optimized_color_ranges']\nexcept Exception:\n    optimized_color_ranges = {\n        'greens': {'r_min':0,'r_max':100,'g_min':50,'g_max':255,'b_min':0,'b_max':150},\n        'grey_yellow': {'r_min':100,'r_max':200,'g_min':100,'g_max':200,'b_min':50,'b_max':150},\n        'earth_tones': {'r_min':80,'r_max':180,'g_min':60,'g_max':140,'b_min':20,'b_max':100}\n    }\nIMG_BASE = '/kaggle/input/csiro-biomass/'\ndef classify_pixels_vectorized(rel_path, cr):\n    full = os.path.join(IMG_BASE, rel_path)\n    if not os.path.exists(full): return {'greens':0.0,'grey_yellow':0.0,'earth_tones':0.0}\n    img = cv2.imread(full);\n    if img is None: return {'greens':0.0,'grey_yellow':0.0,'earth_tones':0.0}\n    rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB); px = rgb.reshape(-1,3)\n    r,g,b = px[:,0], px[:,1], px[:,2]\n    e = (r>=cr['earth_tones']['r_min']) & (r<=cr['earth_tones']['r_max']) & (g>=cr['earth_tones']['g_min']) & (g<=cr['earth_tones']['g_max']) & (b>=cr['earth_tones']['b_min']) & (b<=cr['earth_tones']['b_max'])\n    gy = (~e) & (r>=cr['grey_yellow']['r_min']) & (r<=cr['grey_yellow']['r_max']) & (g>=cr['grey_yellow']['g_min']) & (g<=cr['grey_yellow']['g_max']) & (b>=cr['grey_yellow']['b_min']) & (b<=cr['grey_yellow']['b_max'])\n    gmask = (~e) & (~gy) & (r>=cr['greens']['r_min']) & (r<=cr['greens']['r_max']) & (g>=cr['greens']['g_min']) & (g<=cr['greens']['g_max']) & (b>=cr['greens']['b_min']) & (b<=cr['greens']['b_max'])\n    classified = e | gy | gmask\n    green_count = gmask.sum() + (~classified).sum(); earth_count = e.sum(); grey_count = gy.sum(); total = len(px) or 1\n    return {'greens':(green_count/total)*100.0,'grey_yellow':(grey_count/total)*100.0,'earth_tones':(earth_count/total)*100.0}","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-26T23:21:24.681007Z","iopub.execute_input":"2025-11-26T23:21:24.681353Z","iopub.status.idle":"2025-11-26T23:21:24.692410Z","shell.execute_reply.started":"2025-11-26T23:21:24.681329Z","shell.execute_reply":"2025-11-26T23:21:24.691618Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Build inference feature frame\ntest_long = pd.read_csv(os.path.join(IMG_BASE,'test.csv'))\nimage_paths = test_long['image_path'].unique()\nrows = [classify_pixels_vectorized(p, optimized_color_ranges) for p in image_paths]\ncolor_df = pd.DataFrame(rows)\ncolor_df['image_path'] = image_paths\ncolor_df['Species_encoded'] = 0  # placeholder\nfeature_cols = ['greens','grey_yellow','earth_tones','Species_encoded']\nprint('Feature frame rows:', len(color_df))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-26T23:21:30.034668Z","iopub.execute_input":"2025-11-26T23:21:30.034980Z","iopub.status.idle":"2025-11-26T23:21:30.269643Z","shell.execute_reply.started":"2025-11-26T23:21:30.034958Z","shell.execute_reply":"2025-11-26T23:21:30.268751Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Prepare tensors for prediction\nsample_path = os.path.join(IMG_BASE, image_paths[0])\nsample_img = tf.io.decode_image(tf.io.read_file(sample_path), channels=3, expand_animations=False)\nH,W,C = sample_img.shape\ndef load_image(p):\n    data = tf.io.read_file(os.path.join(IMG_BASE, p))\n    img = tf.io.decode_image(data, channels=3, expand_animations=False)\n    img = tf.image.convert_image_dtype(img, tf.float32)\n    img.set_shape([H,W,C])\n    return img\nimgs = np.stack([load_image(p).numpy() for p in image_paths], axis=0)\nfeats = color_df[feature_cols].values.astype('float32')\nprint('Images tensor:', imgs.shape, 'Features tensor:', feats.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-26T23:21:37.275425Z","iopub.execute_input":"2025-11-26T23:21:37.275744Z","iopub.status.idle":"2025-11-26T23:21:37.458106Z","shell.execute_reply.started":"2025-11-26T23:21:37.275723Z","shell.execute_reply":"2025-11-26T23:21:37.457295Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\"\"\"\ndef classify_pixels_vectorized(image_path, color_ranges):\n    full_path = os.path.join(photo_base, image_path)\n    \n    if not os.path.exists(full_path):\n        return {'greens':0.0,'grey_yellow':0.0,'earth_tones':0.0}\n    img = cv2.imread(full_path)\n    \n    if img is None:\n        return {'greens':0.0,'grey_yellow':0.0,'earth_tones':0.0}\n\n    rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n    px = rgb.reshape(-1,3)\n    r,g,b = px[:,0], px[:,1], px[:,2]\n    cr = color_ranges\n    earth_mask = (r>=cr['earth_tones']['r_min']) & (r<=cr['earth_tones']['r_max']) & (g>=cr['earth_tones']['g_min']) & (g<=cr['earth_tones']['g_max']) & (b>=cr['earth_tones']['b_min']) & (b<=cr['earth_tones']['b_max'])\n    grey_mask = (~earth_mask) & (r>=cr['grey_yellow']['r_min']) & (r<=cr['grey_yellow']['r_max']) & (g>=cr['grey_yellow']['g_min']) & (g<=cr['grey_yellow']['g_max']) & (b>=cr['grey_yellow']['b_min']) & (b<=cr['grey_yellow']['b_max'])\n    green_mask = (~earth_mask) & (~grey_mask) & (r>=cr['greens']['r_min']) & (r<=cr['greens']['r_max']) & (g>=cr['greens']['g_min']) & (g<=cr['greens']['g_max']) & (b>=cr['greens']['b_min']) & (b<=cr['greens']['b_max'])\n    classified = earth_mask | grey_mask | green_mask\n\n    #greens catch-all\n    green_count = green_mask.sum() + (~classified).sum()\n    earth_count = earth_mask.sum()\n    grey_count = grey_mask.sum()\n    total = len(px)\n    \n    if total == 0: return {'greens':0.0,'grey_yellow':0.0,'earth_tones':0.0}\n    return {\n        'greens': (green_count/total)*100.0,\n        'grey_yellow': (grey_count/total)*100.0,\n        'earth_tones': (earth_count/total)*100.0\n}\n\ndef extract_color_features(image_paths, color_ranges):\n    data = []\n    for p in image_paths:\n        feats = classify_pixels_vectorized(p, color_ranges)\n        data.append({'image_path':p,'greens':feats['greens'],'grey_yellow':feats['grey_yellow'],'earth_tones':feats['earth_tones']})\n    return pd.DataFrame(data)\n\"\"\"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Predict\npred_c, pred_d, pred_g = model.predict([imgs, feats], verbose=0)\nscaled_pred = np.concatenate([pred_c, pred_d, pred_g], axis=1)\nidxs = [target_columns.index(t) for t in primary_targets]\nfull_scaled = np.zeros((len(scaled_pred), len(target_columns))); full_scaled[:, idxs] = scaled_pred\ngrams = scaler.inverse_transform(full_scaled)[:, idxs]\nclover_g, dead_g, green_g = grams[:,0], grams[:,1], grams[:,2]\ngdm_g = clover_g + green_g\ndry_total_g = clover_g + dead_g + green_g\nrows = []\nfor i, rel in enumerate(image_paths):\n    img_id = os.path.splitext(os.path.basename(rel))[0]\n    rows.append((f'{img_id}__Dry_Green_g', float(green_g[i])))\n    rows.append((f'{img_id}__Dry_Dead_g', float(dead_g[i])))\n    rows.append((f'{img_id}__Dry_Clover_g', float(clover_g[i])))\n    rows.append((f'{img_id}__GDM_g', float(gdm_g[i])))\n    rows.append((f'{img_id}__Dry_Total_g', float(dry_total_g[i])))\nsubmission_df = pd.DataFrame(rows, columns=['sample_id','target'])\nsubmission_df.to_csv('submission.csv', index=False)\nprint('Saved submission.csv with', len(submission_df), 'rows')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-26T23:21:47.584144Z","iopub.execute_input":"2025-11-26T23:21:47.584489Z","iopub.status.idle":"2025-11-26T23:21:48.088820Z","shell.execute_reply.started":"2025-11-26T23:21:47.584464Z","shell.execute_reply":"2025-11-26T23:21:48.088058Z"}},"outputs":[],"execution_count":null}]}