یادآوری چگونگی سر ریز شدن متغیر ها
با توجه به اینکه برای تصاویر در OpenCV از نوع دادهای unit8 یعنی عدد صحیح بدون علامت ۸ بیتی استفاده میکنیم . پس این نوع ۳ به توان ۸ یعنی ۲۵۶ مقدار مختلف میتواند داشته باشد و مقادیر خارج از ۰ تا ۲۵۵ امکان پذیر نبود و در صورتی که عددی بزرگتر قرار نسبت داده شود متغیر سر ریز میشد.
به قطعه کد زیر توجه کنید.
|
import numpy as np import cv2 a = np.ones([2], dtype = "uint8") b = a * 100 print(a) print(b) print(b + 155) print(b + 160) |
در کد بالا ماتریس دو عنصری a که عناصر آن همه ۱ هستند ، بعد از اینکه عناصرش ۱۰۰ برابر میشوند و به علاوه ۲۵۵می شنود ماتریس [۲۵۵ ۲۵۵] ساخته میشود ولی بعد از افزایش به اندازه ی ۱۶۰ واحد در هر عنصر از ماتریس b ماتریس [۴ ۴] ساخته میشود چون سرریز صورت گرفته ( مقدار بیش از ۲۵۵) و دوباره مقدار مربوطه از ۰ شروع شده است .
خروجی کد فوق :
|
[1 1] [100 100] [255 255] [4 4] |
طبیعتا این موضوع برای جمع دو ماتریس هم اندازه نیز صادق است:
|
import numpy as np m1 = np.array([[150, 155], [156, 157]], dtype = "uint8") m2 = np.ones([2,2], dtype = "uint8") * 100 print(m1 + m2) |
در کد بالا m1 و m2 دو ماتریس به ترتیب به مقادیر :
|
[[150 155] [156 157]] [[100 100] [100 100]] |
هستند.که بعد از عملیات جمع این دو ماتریس دو درایه ی سطر دوم به دلیل سرریز مقادیر ۰ و ۱ را میگیرند.
خروجی کد:
برای حل این مشکل ( خراب نشدن عناصر بر اثر overflow ) راه حل هایی از جمله راه حل زیر وجود دارد :
|
import numpy as np m1 = np.array([[150, 155], [156, 157]], dtype = "uint8") value_to_add = 100 max_threshold = 255 - 100 m1[m1 >= max_threshold] = 255 m1[m1 < max_threshold] += value_to_add print(m1) |
در کد مشاهده شده ماتریس m1 را داریم که می خواهیم مقدار را به تمام درایه های آن بیفزاییم . براثر این عملیات مقادیری از ماتریس اصلی که بیشتر از ۱۵۰ یا مساوی ۱۵۰ هستند وقتی با ۱۰۰ جمع شوند سر ریز میشوند . برای جلوگیری از این موضوع میتوانیم درایه هایی که مقدارشان بیشتر یا مساوی ۱۵۰ هستند برابر با بیشترین مقدار ممکن یعنی ۲۵۵ قرار داده و عملیات جمع را برای بقیه ی درایه ها انجام دهیم .
خروجی کد:
حل مشکل با OpenCV :
اگر از توابع cv2.add و cv2.subtract استفاده کنیم این مشکل را مشاهده نخواهیم کرد و مقادیر کمتر از ۰ همان ۰ و مقادیر بیش از ۲۵۵ همان ۲۵۵ باقی خواهد ماند.
به کد مقابل توجه کنید :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
import cv2 import numpy as np image = cv2.imread('images/input.jpg') M = np.ones(image.shape, dtype = "uint8") * 100 cv2.imshow("Original", image) cv2.waitKey(0) added = cv2.add(image, M) cv2.imshow("Added", added) subtracted = cv2.subtract(image, M) cv2.imshow("Subtracted", subtracted) cv2.waitKey(0) cv2.destroyAllWindows() |
در این قطعه کد تصویری را خواندیم و سپس ماتریسی با اندازه ی تصویر خوانده شده ساختیم که تمام عناصرش ۱۰۰ هستند ، سپس این ماتریس را با تصویرمان جمع کردیم و چون ازتابع add در opencv استفاده کردیم خرابی در تصویر نهایی مشاهده نشد ، و به طور مشابه عمل subtract را انجام دادیم .
تصویر Orginal :

تصویر Add شده :

تصویر Subtract شده :

مثال عملی ترکیب تصاویر رنگی :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
import cv2 import numpy as np img1 = cv2.imread('./images/ml.jpg') img2 = cv2.imread('./images/opencv.jpg') cv2.imshow("ml", img1) cv2.imshow("opencv", img2) cv2.waitKey(0) output_image = cv2.add(img1,img2) cv2.imshow("out", output_image) print(img1.shape) print(img2.shape) print(output_image.shape) cv2.waitKey(0) cv2.destroyAllWindows() |
در این مثال عملی مشاهده میشود که بعد از خواندن دو تصویر رنگی با نام های img1 و img2 ، این دو تصویر را با استفاده از تابع add جمع کردیم و در تصویر نهایی از دست رفتگی بعضی پیکسل ها را داریم ( همانطور که در زیر مشاهده میکنید مثلا تصویر img2 برخی قسمت هایش از دست رفته است).
img1 :
img2 :

result :

حل این مشکل با ترکیب دو تصویر با جمع وزندار :
برای حل این مشکل میتوانیم با نسبت های مختلف ، مثلا ۰.۵ و ۰.۵ یا ۰.۳ و ۰.۷ و یا…. نسبت هایی که مشابها جمعشان ۱ گردد تصاویر را جمع کنیم .
تابع مورد نظر به شکل زیر می باشد :
addWeighted(src1, alpha, src2, beta, gamma)
که آلفا و بتا در فرمول دوم مشاهده میشود.
g(x)=(1−α)f0(x)+αf1(x)
dst=α⋅img1+β⋅img2+γ
با این عمل میتوانیم تاثیر هر تصویر را بعد از عملیات جمع در تصویر نهایی دستکاری کنیم :
|
import cv2 import numpy as np img1 = cv2.imread('./images/ml.jpg') img2 = cv2.imread('./images/opencv.jpg') dst = cv2.addWeighted(img1,0.7,img2,0.3,0) cv2.imshow('dst',dst) cv2.waitKey(0) cv2.destroyAllWindows() |
img1 :
img2 :

result :

عملیات بیتی (Bitwise Operations) و نقابگذاری تصویر (Masking) :
ابتدا به رسم یک مربع و نیم دایره برای پیاده سازی عملیات بیتی روی اشکال میپردازیم.
|
import cv2 import numpy as np square = np.zeros((300, 300), np.uint8) cv2.rectangle(square, (50, 50), (250, 250), 255, -1) cv2.imshow("Square", square) cv2.waitKey(0) ellipse = np.zeros((300, 300), np.uint8) cv2.ellipse(ellipse, (150, 150), (150, 150), 30, 0, 180, 255, -1) cv2.imshow("Ellipse", ellipse) cv2.waitKey(0) cv2.destroyAllWindows() |
همانطور که قابل مشاهده است ، در صفحه ای به اندازه ی ۳۰۰ در ۳۰۰ که تمام عناصر ماتریس آن صفر هستند ( صفحه ی تمام سیاه ) یک مربع سفید در وسط صفحه از مختصات (۵۰ ، ۵۰) تا (۲۵۰ ، ۲۵۰) رسم میکنیم ، سپس در صفحه ای مشابه به رسم نیم دایره میپردازیم.
تصاویر به این شکل خواهند بود :


عملیات بیتی تصویر در OpenCV :
حال به بررسی ۴ عملیات
- bitwise_and
- bitwise_or
- bitwise_xor
- bitwise_not
روی تصاویر خواهیم پرداخت :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
# Shows only where they intersect And = cv2.bitwise_and(square, ellipse) cv2.imshow("AND", And) cv2.waitKey(0) # Shows where either square or ellipse is bitwiseOr = cv2.bitwise_or(square, ellipse) cv2.imshow("OR", bitwiseOr) cv2.waitKey(0) # Shows where either exist by itself bitwiseXor = cv2.bitwise_xor(square, ellipse) cv2.imshow("XOR", bitwiseXor) cv2.waitKey(0) # Shows everything that isn't part of the square bitwiseNot_sq = cv2.bitwise_not(square) cv2.imshow("NOT - square", bitwiseNot_sq) cv2.waitKey(0) ### Notice the last operation inverts the image totally cv2.destroyAllWindows() |
عملیات bitwise_and :
در نتیجه ی عملیات and بین دو تصویر قسمت هایی که پیکسل در هر دوی آن تصاویر ۱ ( سفید ) است ، سفید ( مقدار 1 ) باقی خواهد ماند ، و بقیه ی پیکسل ها صفر ( سیاه ) خواهند بود و نتیجه به این شکل خواهد بود :

عملیات bitwise_or :
در نتیجه ی این عملیات قسمت هایی از تصویر نهایی سفید ( مقدار یک ) خواهد بود که مربع یا نیم دایره در آن نقاط سفید باشند.
و نتیجه به این شکل خواهد بود :

عملیات bitwise_xor :
در نتیجه ی عملیات xor ، قسمت هایی از تصویر که مربع و نیم دایره تفاوت بیتی ( یکی صفر و دیگری یک ؛ یکی سیاه و دیگری سفید ) دارند ، سفید می شود.
نتیجه شکل زیر خواهد بود :

عملیات bitwise_not :
این تابع نیز قسمت هایی از تصویر که بیت ۰ دارند ۱ میکند و بلعکس ( پیکسل های سیاه را سفید میکند و پیکسل های سفید را سیاه )

مثال عملی با تصویر رنگی :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
import cv2 import numpy as np image = cv2.imread('./images/input.jpg') cropped = image[100:600 , 150:650] cv2.imshow("Beautiful Cow!", cropped) cv2.waitKey(0) circle = np.zeros((500,500,3), np.uint8) cv2.circle(circle, (250, 250), 250, (255,255,255), -1) cv2.imshow("Circle", circle) cv2.waitKey(0) output_image = cv2.bitwise_and(cropped, circle) cv2.imshow("Output Image", output_image) cv2.waitKey(0) cv2.destroyAllWindows() |
اگر تصویر گاو اصلی را crop کنیم و سپس با این تصویر and کنیم :

با توجه به اینکه میدانیم هر چیزی که با ۱ ، and شود ، حاصل خود آن میشود ، و هرچیزی که با ۰ ، and شود حاصل ۰ خواهد بود پس در نتیجه تصویر به این شکل در خواهد آمد :
همانطور که مشاهده میشود پیکسل هایی از تصویر گاو که در تناظر با قسمت های سیاه تصویر دایره هستند ، از بین خواهند رفت.(سیاه میشوند)
برای درک عملگر بیتی دو تصویر به مثال زیر توجه کنید :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
square = np.zeros((10, 10), np.uint8) cv2.rectangle(square, (2, 2), (8, 8), 2, -1) # 2 : 010 circle1 = np.zeros((10, 10), np.uint8) cv2.circle(circle1, (5, 5), 2, 3, -1) # 3 : 011 circle2 = np.zeros((10, 10), np.uint8) cv2.circle(circle2, (5, 5), 2, 4, -1) # 4 : 100 output1 = cv2.bitwise_and(square, circle1) output2 = cv2.bitwise_and(square, circle2) print("--square--") print(square) print("--circle1--") print(circle1) print("--circle2--") print(circle2) print("--output1--") print(output1) print("--output2--") print(output2) |
خروجی به شکل زیر خواهد بود :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
|
--square-- [[0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0] [0 0 2 2 2 2 2 2 2 0] [0 0 2 2 2 2 2 2 2 0] [0 0 2 2 2 2 2 2 2 0] [0 0 2 2 2 2 2 2 2 0] [0 0 2 2 2 2 2 2 2 0] [0 0 2 2 2 2 2 2 2 0] [0 0 2 2 2 2 2 2 2 0] [0 0 0 0 0 0 0 0 0 0]] --circle1-- [[0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 3 0 0 0 0] [0 0 0 0 3 3 3 0 0 0] [0 0 0 3 3 3 3 3 0 0] [0 0 0 0 3 3 3 0 0 0] [0 0 0 0 0 3 0 0 0 0] [0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0]] --circle2-- [[0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 4 0 0 0 0] [0 0 0 0 4 4 4 0 0 0] [0 0 0 4 4 4 4 4 0 0] [0 0 0 0 4 4 4 0 0 0] [0 0 0 0 0 4 0 0 0 0] [0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0]] --output1-- [[0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 2 0 0 0 0] [0 0 0 0 2 2 2 0 0 0] [0 0 0 2 2 2 2 2 0 0] [0 0 0 0 2 2 2 0 0 0] [0 0 0 0 0 2 0 0 0 0] [0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0]] --output2-- [[0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0]] |
شبکه های اجتماعی