{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Crop1\n\n","metadata":{}},{"cell_type":"markdown","source":"This challenge required to find an input to a neural network that would ouput a score (from a scoring function provided by the organizers) lower than 1e7. \n\nMy approach was understanding the scoring function, first by understanding X-comp a square with a circle inside and the scoring function that used the model to crop to 1 of 9 circle from a grid of 3x3 circles.\n\nThe problem from a 224x224x3 input image size was reducible to a 18 rgb values so 54 int values between 0 and 255. \n\nAfter knowing that it was straightforward to get the required score by iteratively improving the 54 integers, this was done by adding a random noise and testing the score function keeping only the noises that improved the score. \n\nTo consider a new score an improvement I considered the 9 possible crop regions as independent so getting 1e10 in circle 0 while the best score for circle 0 is 1e11 was considered an improvement even if we already saw a score of 1e8 in circle 8.","metadata":{}},{"cell_type":"code","source":"\nimport pandas as pd\nimport numpy as np\nimport os\nimport requests\nfrom IPython.display import Image as Ima\nfrom IPython.display import clear_output, display\nfrom PIL import Image as Im\nfrom torch.utils.data import Dataset, DataLoader\nimport torch\nimport joblib\nimport matplotlib.pyplot as plt\nimport json\nimport base64\nimport shutil\nimport warnings\nwarnings.filterwarnings(\"ignore\")\n\n\ndir_name = \"/kaggle/input/ai-village-ctf\"\n\n","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2022-09-11T23:03:21.281696Z","iopub.execute_input":"2022-09-11T23:03:21.282501Z","iopub.status.idle":"2022-09-11T23:03:23.261308Z","shell.execute_reply.started":"2022-09-11T23:03:21.282407Z","shell.execute_reply":"2022-09-11T23:03:23.259378Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def add_flag(challenge_id: str, flag: str) -> str:\n    try:\n        submission_path = \"/kaggle/working/submission.csv\"\n        template_path = f\"{dir_name}/submission.csv\"\n        if not os.path.isfile(submission_path) :\n            shutil.copyfile(template_path, submission_path)\n        flags = pd.read_csv(submission_path, index_col=\"challenge_id\")\n        valid = flags.index.to_list()\n        if challenge_id in valid:\n            flags.at[challenge_id] = flag\n            flags.to_csv(submission_path)\n            print(\"Flag saved. If you want to update the scoreboard, click 'submit' on the right. Remember that you can only update your score on the scoreboard 50 times per day\")\n        else:\n            print(\"Invalid challenge_id\")\n    except:\n        print(\"We had trouble saving that flag. Keep a copy of it and contact the CTF organizers.\")","metadata":{"_kg_hide-input":true,"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2022-09-11T23:03:23.263661Z","iopub.execute_input":"2022-09-11T23:03:23.264555Z","iopub.status.idle":"2022-09-11T23:03:23.277778Z","shell.execute_reply.started":"2022-09-11T23:03:23.264500Z","shell.execute_reply":"2022-09-11T23:03:23.276258Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Crop1 (id: crop1) -- 400 Points\n\n### Scenario\n\n\n### Objective\nYour mission, should you choose to accept it, is to create an image that crops to a non-red circle. Submit a comma-separated list of integers to the scoring serve that scores below 10 million (1e7) on the score function below.","metadata":{}},{"cell_type":"code","source":"%%capture\nmodel = joblib.load(f\"{dir_name}/crop/04JUN_crop_model.pckl\")\nX_comp = joblib.load(f\"{dir_name}/crop/X_comp.pckl\")","metadata":{"execution":{"iopub.status.busy":"2022-09-11T23:03:23.280299Z","iopub.execute_input":"2022-09-11T23:03:23.280999Z","iopub.status.idle":"2022-09-11T23:03:40.015983Z","shell.execute_reply.started":"2022-09-11T23:03:23.280953Z","shell.execute_reply":"2022-09-11T23:03:40.014573Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"X_comp.shape","metadata":{"execution":{"iopub.status.busy":"2022-09-11T23:03:40.019419Z","iopub.execute_input":"2022-09-11T23:03:40.019783Z","iopub.status.idle":"2022-09-11T23:03:40.029638Z","shell.execute_reply.started":"2022-09-11T23:03:40.019749Z","shell.execute_reply":"2022-09-11T23:03:40.028078Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Helper Functions\ndef get_ij(idx):\n    idx = 8-idx\n    j = idx//3\n    i = 2-idx%3\n    return i, j\n\ndef resize(X, size=(224,224)):\n    X_new = np.array(Im.fromarray(X.astype(np.uint8)).resize(size, resample=3))\n    return X_new\n\ndef get_cropped(X, idx):\n    n = 75*3\n    i, j = get_ij(idx)\n    crop = resize(X,(n,n))[j*75:(j+1)*75,i*75:(i+1)*75,:]\n    return crop\n\ndef i2t(img:np.array):\n    return np.array(img, dtype=np.float32)/255.\n\ndef make_X(x, size=(224,224)):\n    side = int((len(x)//3)**0.5)\n    #print(side)\n    x = np.where(x<255,x,255)\n    x = np.where(x>0,x,0)\n    X_np = x.astype(int).reshape([side, side, 3])\n    X_np = np.array(Im.fromarray(X_np.astype(np.uint8)).resize(size, resample=Im.Resampling.NEAREST))\n    return X_np\n\ndef pt_ft(X_np):\n    return torch.FloatTensor(X_np)\n\ndef score(x, model=model,already_correct_size=False, X_comp = np.divide(X_comp,0b1010), debug=False, return_8=False):\n    x = (x).astype(int)\n    model.eval()\n    if not already_correct_size:\n        X = make_X(x)\n    else:\n        X=x\n    preds = model(pt_ft(i2t(X.T[None])))\n    if not  return_8:\n        idx = preds.argmax().item()\n    else:\n        idx = preds.argmax().item()\n        preds = preds.detach().numpy()[0]\n    expected = ((25.5-X_comp)).flatten()\n    \n    if True:\n        sse_s=[]\n        redness_s=[]\n        score_s=[]\n        for i in range(9):\n            actual_square = get_cropped(X, i)\n            actual = actual_square.flatten().astype(int)\n            sse = ((actual-(expected*10).astype(int))**2).sum()\n            redness = (actual_square[:,:,0][actual_square[:,:,0]>230]**2).sum()+1\n            sse_s.append(sse)\n            redness_s.append(redness)\n            score_s.append(redness*sse if idx!=8 else (1+preds.max().item())*sse*redness)\n            #print(i,sse,redness)\n            #plt.imshow(actual_square)\n            #plt.show()\n        if debug:\n            print(sse_s,redness_s,score_s)\n        \n    actual_square = get_cropped(X, idx)\n    actual = actual_square.flatten().astype(int)\n    sse = ((actual-(expected*10).astype(int))**2).sum()\n    redness = (actual_square[:,:,0][actual_square[:,:,0]>230]**2).sum()+1\n    if idx==8:\n        return (1+preds.max().item())*sse*redness ,preds,np.array(score_s)\n    return sse*redness , preds,np.array(score_s)\n\ndef get_submittable(x):\n    return \",\".join([str(int(n)) for n in x])\n\ncfn = lambda x: [f\"background-color: RGB({x.R},{x.G},{x.B})\"] * 3\ndef show_colors(ans):\n    ans = [int(x) for x in ans.split(\",\")]\n    squares = pd.DataFrame(np.array(ans[:27]).reshape([9,3]), columns=[\"R\",\"G\",\"B\"]).T.style.apply(cfn, axis=0)\n    circles = pd.DataFrame(np.array(ans[27:]).reshape([9,3]), columns=[\"R\",\"G\",\"B\"]).T.style.apply(cfn, axis=0)\n    display(squares)\n    display(circles)","metadata":{"execution":{"iopub.status.busy":"2022-09-11T23:03:40.031020Z","iopub.execute_input":"2022-09-11T23:03:40.031550Z","iopub.status.idle":"2022-09-11T23:03:40.058431Z","shell.execute_reply.started":"2022-09-11T23:03:40.031349Z","shell.execute_reply":"2022-09-11T23:03:40.056972Z"},"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#target colors and shape\ndef get_base():\n    return (25.5-np.divide(X_comp,10))*10\ndef get_mask():\n    square_with_circle=get_base()\n    red,green,blue=square_with_circle[:,:,0],square_with_circle[:,:,1],square_with_circle[:,:,2]\n    r,g,b=251,128,114\n    distance=10\n    mask= (np.abs(red-r)+np.abs(green-g)+np.abs(blue-b)<distance) \n\n    #mask=mask.astype(int)\n    return mask\ndef create_square(base,mask,r,g,b,r1,g1,b1):\n    #print(mask[-1,:],mask.shape,base.shape)\n    temp=base.copy()\n    temp[:,:,:3][mask]=[r,g,b]\n    temp[:,:,:3][~mask]=[r1,g1,b1]\n    return temp\ndef get_submittable_image(squares):\n    ans_row_0=np.hstack([squares[i] for i in range(3)])\n    ans_row_1=np.hstack([squares[i] for i in range(3,6)])\n    ans_row_2=np.hstack([squares[i] for i in range(6,9)])\n    return np.vstack([ans_row_2,ans_row_1,ans_row_0])\ndef get_unique(img):\n    return np.unique(img.reshape(-1, img.shape[2]), axis=0)\n\ncolor1=(251,128,114)\ncolor2=(180,220,105)\nr,g,b=color1\nr1,g1,b1=color2","metadata":{"execution":{"iopub.status.busy":"2022-09-11T23:03:40.060310Z","iopub.execute_input":"2022-09-11T23:03:40.060746Z","iopub.status.idle":"2022-09-11T23:03:40.076328Z","shell.execute_reply.started":"2022-09-11T23:03:40.060711Z","shell.execute_reply":"2022-09-11T23:03:40.075024Z"},"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_initial_colors(color1,color2):\n    colors_squares=[]\n    colors_circles=[]\n    for i in range(9):\n        colors_squares.extend(color1)\n        colors_circles.extend(color2)\n    colors_squares=np.array(colors_squares)\n    colors_circles=np.array(colors_circles)\n    return np.concatenate([colors_squares,colors_circles])\ndef colors_to_sub(base,mask,colors):\n    squares=[]\n    for i in range(9):\n        squares.append(create_square(base,mask,colors[i*3],colors[i*3+1],colors[i*3+2],colors[i*3+27],colors[i*3+28],colors[i*3+29]))\n    return get_submittable_image(squares)","metadata":{"execution":{"iopub.status.busy":"2022-09-11T23:03:40.078289Z","iopub.execute_input":"2022-09-11T23:03:40.078716Z","iopub.status.idle":"2022-09-11T23:03:40.089145Z","shell.execute_reply.started":"2022-09-11T23:03:40.078684Z","shell.execute_reply":"2022-09-11T23:03:40.088217Z"},"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"base=get_base()\nmask=get_mask()","metadata":{"execution":{"iopub.status.busy":"2022-09-11T23:03:40.090556Z","iopub.execute_input":"2022-09-11T23:03:40.091472Z","iopub.status.idle":"2022-09-11T23:03:40.108241Z","shell.execute_reply.started":"2022-09-11T23:03:40.091424Z","shell.execute_reply":"2022-09-11T23:03:40.106634Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.imshow(mask)","metadata":{"execution":{"iopub.status.busy":"2022-09-11T23:03:40.109895Z","iopub.execute_input":"2022-09-11T23:03:40.110341Z","iopub.status.idle":"2022-09-11T23:03:40.348774Z","shell.execute_reply.started":"2022-09-11T23:03:40.110278Z","shell.execute_reply":"2022-09-11T23:03:40.347582Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"mask.shape","metadata":{"execution":{"iopub.status.busy":"2022-09-11T23:03:40.352433Z","iopub.execute_input":"2022-09-11T23:03:40.352808Z","iopub.status.idle":"2022-09-11T23:03:40.360256Z","shell.execute_reply.started":"2022-09-11T23:03:40.352774Z","shell.execute_reply":"2022-09-11T23:03:40.358927Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"colors=get_initial_colors(color1,color2)","metadata":{"execution":{"iopub.status.busy":"2022-09-11T23:03:40.362441Z","iopub.execute_input":"2022-09-11T23:03:40.362781Z","iopub.status.idle":"2022-09-11T23:03:40.368388Z","shell.execute_reply.started":"2022-09-11T23:03:40.362750Z","shell.execute_reply":"2022-09-11T23:03:40.367252Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.imshow(colors_to_sub(base,mask,colors).astype(\"uint8\"))","metadata":{"execution":{"iopub.status.busy":"2022-09-11T23:03:40.369802Z","iopub.execute_input":"2022-09-11T23:03:40.370472Z","iopub.status.idle":"2022-09-11T23:03:40.586935Z","shell.execute_reply.started":"2022-09-11T23:03:40.370429Z","shell.execute_reply":"2022-09-11T23:03:40.586021Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from tqdm import tqdm\n\n\ntarget_score=10000000\n\nreached_high_logit=False\n\nX_small = colors\n\n#to save the index of the valid updates -> so we can replicate avoiding the random trials\n\nlist_of_updates=[]\n    #rescaling an image\nbase_image = X_small\nbest={}\nfor i in range(350000):\n    #break\n    np.random.seed(161194+i)\n    # creating an image\n    \n    X_small = base_image + np.random.normal(0,30,size=base_image.shape)\n    X_small = np.clip(X_small,a_min=0,a_max=255)\n    \n    im = np.array(get_submittable(colors_to_sub(base,mask,X_small).astype(\"uint8\").flatten()).split(\",\"))\n    \n    real_output ,preds ,scores=score(im,return_8=True)\n    \n    index=preds.argmax()\n    \n    if real_output<target_score:\n        list_of_updates.append(i)\n        target=X_small\n        print(i,\" found \",real_output)\n        break\n    worst=scores.max()\n    if index not in best or best[index]>real_output:\n        best[index]=real_output\n        list_of_updates.append(i)\n        base_image=X_small\n        print(index,\" improved_redness \", real_output,\" worst-\",worst)\n        ","metadata":{"scrolled":true,"execution":{"iopub.status.busy":"2022-09-11T23:03:40.588530Z","iopub.execute_input":"2022-09-11T23:03:40.589179Z","iopub.status.idle":"2022-09-11T23:09:06.782620Z","shell.execute_reply.started":"2022-09-11T23:03:40.589130Z","shell.execute_reply":"2022-09-11T23:09:06.781246Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"im = np.array(get_submittable(colors_to_sub(base,mask,target).astype(\"uint8\").flatten()).split(\",\"))\n    \nreal_output ,preds ,scores=score(im,return_8=True)","metadata":{"execution":{"iopub.status.busy":"2022-09-11T23:09:06.784344Z","iopub.execute_input":"2022-09-11T23:09:06.785559Z","iopub.status.idle":"2022-09-11T23:09:07.065823Z","shell.execute_reply.started":"2022-09-11T23:09:06.785512Z","shell.execute_reply":"2022-09-11T23:09:07.064682Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"im = im.astype(int)\nrequired_value = get_submittable(make_X(im).astype(\"uint8\").flatten())","metadata":{"execution":{"iopub.status.busy":"2022-09-11T23:09:07.067445Z","iopub.execute_input":"2022-09-11T23:09:07.068219Z","iopub.status.idle":"2022-09-11T23:09:07.186912Z","shell.execute_reply.started":"2022-09-11T23:09:07.068154Z","shell.execute_reply":"2022-09-11T23:09:07.185768Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"real_output","metadata":{"execution":{"iopub.status.busy":"2022-09-11T23:09:07.188337Z","iopub.execute_input":"2022-09-11T23:09:07.188791Z","iopub.status.idle":"2022-09-11T23:09:07.196471Z","shell.execute_reply.started":"2022-09-11T23:09:07.188746Z","shell.execute_reply":"2022-09-11T23:09:07.195237Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"if True:\n    real_output ,preds ,scores=score(im,debug=True)\nreal_output","metadata":{"execution":{"iopub.status.busy":"2022-09-11T23:09:07.198341Z","iopub.execute_input":"2022-09-11T23:09:07.198788Z","iopub.status.idle":"2022-09-11T23:09:07.333380Z","shell.execute_reply.started":"2022-09-11T23:09:07.198745Z","shell.execute_reply":"2022-09-11T23:09:07.332242Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"string=json.dumps({\"data\":required_value})\nwith open(\"crop1.txt\",\"w+\") as f:\n    f.write(string)","metadata":{"execution":{"iopub.status.busy":"2022-09-11T23:09:07.335179Z","iopub.execute_input":"2022-09-11T23:09:07.335999Z","iopub.status.idle":"2022-09-11T23:09:07.345712Z","shell.execute_reply.started":"2022-09-11T23:09:07.335942Z","shell.execute_reply":"2022-09-11T23:09:07.344383Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Submission Helper\nwith open(\"crop1.txt\") as f:\n    s=f.read()\ntry:\n    r = requests.post(\"https://crop1.fly.dev/score\", data=s)\n    print(r.text)\nexcept requests.exceptions.ConnectionError:\n    print(\"Connection problems. Contact the CTF organizers.\")","metadata":{"execution":{"iopub.status.busy":"2022-09-11T23:09:07.347489Z","iopub.execute_input":"2022-09-11T23:09:07.348594Z","iopub.status.idle":"2022-09-11T23:09:08.293931Z","shell.execute_reply.started":"2022-09-11T23:09:07.348559Z","shell.execute_reply":"2022-09-11T23:09:08.292740Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"add_flag(\"crop1\", r.text.split(\"champion. \")[1].split(\"\\\",\")[0])\nr.text.split(\"champion. \")[1].split(\"\\\",\")[0]","metadata":{"execution":{"iopub.status.busy":"2022-09-11T23:09:08.295343Z","iopub.execute_input":"2022-09-11T23:09:08.296276Z","iopub.status.idle":"2022-09-11T23:09:08.332305Z","shell.execute_reply.started":"2022-09-11T23:09:08.296228Z","shell.execute_reply":"2022-09-11T23:09:08.331364Z"},"trusted":true},"execution_count":null,"outputs":[]}]}