zadanieanalizalab5/Lab_5.ipynb

160 lines
2.5 MiB
Plaintext
Raw Normal View History

2024-11-16 20:03:51 +01:00
{
"cells": [
{
"cell_type": "markdown",
"id": "c36cf7d5-c332-4482-af0e-3ba30ea9bb8e",
"metadata": {},
"source": [
"<b>Uwaga:</b> Należy rozwiązać oba poniższe zadania. Na zaliczenie potrzeba co najmniej 3 pkt. \n",
"<p></p>\n",
"\n",
"1. <b>Zadanie 1.</b>(2x1.5 pkt): Zreplikować wyniki związane z materiałem na stronie <a href=\"https://thepythoncodingbook.com/2021/08/30/2d-fourier-transform-in-python-and-fourier-synthesis-of-images/\">2d fourier transform in python and fourier synthesis of images</a>. Istotą tego zadania jest poprawne zrozumienie działania transformaty Fouriera na obrazach - jego zaliczenie będzie polegało na odpowiedzi na dwa pytania teoretyczne.\n",
"\n",
"2. <b>Zadanie 2.</b>(2x1 pkt): Napisać kod dwóch filtrów opartych o transformatę Fouriera: dolno i górnoprzepustowego. W pierwszym przypadku przykładowy obraz dobrej jakości zaburzyć kolejno trzema różnymi szumami dostępnyhmi w programie Gimp. W każdym przypadku zastosować filtr dolnoprzepustowy możliwie dokładnie przywracający obraz przed zaszumieniem. Następnie wygenerować filtr górnoprzepustowy wizualizujący w trzech powyższych przypadkach postać dodanego szumu. Materiał referencyjny znajduje się m.in. na stronie\n",
"<a href=\"https://fairyonice.github.io/Low-and-High-pass-filtering-experiments.html\">Low and High pass filtering experiments.html</a>. "
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "7b39ab4f",
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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
"text/plain": [
"<Figure size 640x480 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers). Got range [5.757753222571058e-06..1.1031931990186616].\n"
]
},
{
"data": {
"image/png": "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4447jm222YZ169bxrGc9i+Xl5VXr3Noc6V/60pc49NBDGY/H7Lbbbrz61a/mXe961xZxzmY+85nPcMABBzAajbjXve7Fe97znp84ts05xde//vW88Y1vZPfdd2c8HnPooYfy7//+71uMZ2vioa2JOa+66iqe/OQns/POOzMajdhtt9045phjuOmmm37kWPfYYw9e/vKXA7DDDjsgIivd5T88R/oPc9xxx63kRDfHpLeMzVSVs846i/vd736MRiN22mknjj/+eG688cYtxnDEEUfw8Y9/fCWmPPfcc4HyPZ988snc4x73oOs69tprL1772teiqqs+Y/P5sG7dOrbZZhuOPfbYVc8YP44fl4/efL5MJhP22msvLrzwQgD+4R/+gYc+9KGMx2P22WcfLr300lWfec0113DCCSewzz77MB6P2X777TnqqKNu9Ry7Lefk1sT5lcovO7UjvVKp/Ez5yle+wsEHH8zatWt5yUteQtM0nHvuuRx22GErAcItOeGEE9hhhx142ctextLS0o/83FNPPZUzzzyTJzzhCaxfv55/+7d/Y/369cxms60a14033sjhhx/Ok570JI4++mguvPBC/uiP/oj99tuPxz72sQDcfPPNnHfeeTztaU/j2c9+Nhs3buSd73wn69ev55//+Z954AMfeLuPyy3JOfPYxz6WQw45hDPPPJMLLriA5z//+SwsLPC//tf/4rd/+7d50pOexNvf/nae+cxn8rCHPYw999wTgP/8z//kwx/+MEcddRR77rkn3/ve9zj33HM59NBD+epXv8ouu+wClG6gRz7ykfzP//wPL3jBC9h555153/ved6uigL//+7/nsY99LPvvvz8vf/nLcc7xrne9i0c+8pFcdtllHHDAAXfIflcqlUqlUqncFl7xildw2mmn8ahHPYrnPve5XHnllZxzzjl84Qtf4J/+6Z9omoaDDz4YVeUzn/kMRxxxBACXXXYZzjkuu+yylc+64oor2LRpE4cccshP3O5VV13FU5/6VJ7znOdw7LHH8q53vYujjjqKiy++mEc/+tHA1sdk73jHOzjppJN4ylOewgte8AJmsxlf+tKX+PznP8/Tn/50AJ7znOdw4YUX8vznP599992X66+/ns985jN87Wtf40EPetCtjvH4449n11135fTTT+ekk07iIQ95CDvttNNWHddDDjmEk046iTe/+c289KUv5b73vS/Ayn/f+973cuyxx7J+/Xpe+9rXsry8zDnnnMNBBx3EFVdcscoKPqXE+vXrOeigg3j9619/m+bD3Mw3vvENnv70p3P88cfzjGc8g9e//vU84QlP4O1vfzsvfelLOeGEEwA444wzOProo1dZrH7iE5/gP//zP3nWs57FzjvvzFe+8hX+/M//nK985St87nOfW0nEXnHFFRx++OHc/e5357TTTiPnzCtf+Up22GGHLcbzmte8hj/5kz/h6KOP5vd///f5/ve/z1ve8hYOOeQQrrjiii06rSqVSqVSqfxicvTRR7Pnnntyxhln8K//+q+cd9557Ljjjrz2ta/9ke+59tprV4SIp556KgsLC5x33nk/snP9G9/4Bk95ylP4vd/7PY499lj+4i/+guOOO47999+f+93vfj9xjO95z3vYuHEjz3ve85jNZrzpTW/ikY98JF/+8pdXYr+
"text/plain": [
"<Figure size 2500x1800 with 3 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"import matplotlib.pyplot as plt\n",
"import numpy as np\n",
"img = plt.imread(\"wmi1.jpg\")/float(2**8)\n",
"plt.imshow(img)\n",
"plt.show()\n",
"\n",
"shape = img.shape[:2]\n",
"\n",
"def draw_cicle(shape,diamiter):\n",
" assert len(shape) == 2\n",
" TF = np.zeros(shape,dtype=np.bool)\n",
" center = np.array(TF.shape)/2.0\n",
"\n",
" for iy in range(shape[0]):\n",
" for ix in range(shape[1]):\n",
" TF[iy,ix] = (iy- center[0])**2 + (ix - center[1])**2 < diamiter **2\n",
" return(TF)\n",
"\n",
"\n",
"TFcircleIN = draw_cicle(shape=img.shape[:2],diamiter=150)\n",
"TFcircleOUT = ~TFcircleIN\n",
"\n",
"\n",
"\n",
"fft_img = np.zeros_like(img,dtype=complex)\n",
"for ichannel in range(fft_img.shape[2]):\n",
" fft_img[:,:,ichannel] = np.fft.fftshift(np.fft.fft2(img[:,:,ichannel]))\n",
"\n",
"\n",
"\n",
"def filter_circle(TFcircleIN,fft_img_channel):\n",
" temp = np.zeros(fft_img_channel.shape[:2],dtype=complex)\n",
" temp[TFcircleIN] = fft_img_channel[TFcircleIN]\n",
" return(temp)\n",
"\n",
"fft_img_filtered_IN = []\n",
"fft_img_filtered_OUT = []\n",
"## for each channel, pass filter\n",
"for ichannel in range(fft_img.shape[2]):\n",
" fft_img_channel = fft_img[:,:,ichannel]\n",
" ## circle IN\n",
" temp = filter_circle(TFcircleIN,fft_img_channel)\n",
" fft_img_filtered_IN.append(temp)\n",
" ## circle OUT\n",
" temp = filter_circle(TFcircleOUT,fft_img_channel)\n",
" fft_img_filtered_OUT.append(temp) \n",
" \n",
"fft_img_filtered_IN = np.array(fft_img_filtered_IN)\n",
"fft_img_filtered_IN = np.transpose(fft_img_filtered_IN,(1,2,0))\n",
"fft_img_filtered_OUT = np.array(fft_img_filtered_OUT)\n",
"fft_img_filtered_OUT = np.transpose(fft_img_filtered_OUT,(1,2,0))\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"def inv_FFT_all_channel(fft_img):\n",
" img_reco = []\n",
" for ichannel in range(fft_img.shape[2]):\n",
" img_reco.append(np.fft.ifft2(np.fft.ifftshift(fft_img[:,:,ichannel])))\n",
" img_reco = np.array(img_reco)\n",
" img_reco = np.transpose(img_reco,(1,2,0))\n",
" return(img_reco)\n",
"\n",
"\n",
"img_reco = inv_FFT_all_channel(fft_img)\n",
"img_reco_filtered_IN = inv_FFT_all_channel(fft_img_filtered_IN)\n",
"img_reco_filtered_OUT = inv_FFT_all_channel(fft_img_filtered_OUT)\n",
"\n",
"fig = plt.figure(figsize=(25,18))\n",
"ax = fig.add_subplot(1,3,1)\n",
"ax.imshow(np.abs(img_reco))\n",
"ax.set_title(\"original image\")\n",
"\n",
"ax = fig.add_subplot(1,3,2)\n",
"ax.imshow(np.abs(img_reco_filtered_IN))\n",
"ax.set_title(\"low pass filter image\")\n",
"\n",
"\n",
"ax = fig.add_subplot(1,3,3)\n",
"ax.imshow(np.abs(img_reco_filtered_OUT))\n",
"ax.set_title(\"high pass filtered image\")\n",
"plt.show()"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.13.0"
}
},
"nbformat": 4,
"nbformat_minor": 5
}