Bitwise operatsiyalari kompyuter fanlari va dasturlashning qorong'u tomonida yotadi. Bular sirli mavjudotlar, biroz qo'rqinchli, lekin ba'zida juda foydali. Ushbu maqolada ular kompyuterni ko'rish sohasiga qanday qo'llanilishi va ularni OpenCV va Python bilan qanday ishlatishingiz mumkinligini tushuntirishga qaratilgan.

Umid qilamanki, sizga foydali bo'ladi!

Maskalarni kiritishdan oldin ularni BGR tasvirlari bilan qanday ishlatishimiz mumkinligini ko'rish uchun biz ushbu operatsiyalarni ikkilik tasvirlarga qo'llashdan boshlaymiz.

Qani ketdik!

Bitli operatsiyalar nima qilishini va qanday ishlashini tasavvur qilish va tushunishning eng oddiy usuli bu ikkilik tasvirlarda amalga oshirishi mumkin bo'lgan o'zgarishlarni ko'rib chiqishdir.
OpenCVda to'rtta bitli operatsiyalar mavjud: va, yoki, eksklyuziv yoki — koʻpincha xor — va yoʻq qayd etiladi. Va, or va xor hammasi ikkita bitni talab qiladi va ularning haqiqat jadvaliga muvofiq bir bitni chiqaradi. Not bitta bitni oladi va oʻzining “teskarisi”ni qaytaradi: 0 1 ga aylanadi va aksincha.
Ikkilik tasvirlar faqat 0 (odatda qora piksel) va 255 (odatda) dan iborat matritsalardir. oq piksellar), ular bilan ikkilik operatsiyalarning ishlash usuli juda oddiy: ular bajaradigan yagona narsa har bir pikselga bit operatsiyalarini qo'llashdir.

Quyidagi ikkita rasm bilan to'rtta bitli operatsiyani tasvirlab beraman:

Ular quyidagi kod yordamida yaratilgan:

import numpy as np
import cv2 as cv
# we create a black image of 400*400 pixels and draw a 100*100 white square in the middle.
black_one = np.zeros((400, 400), dtype=np.uint8)
black_one[150:250, 150:250] = 255
# same process, except that the white square has been gently pushed towards the bottom-right corner.
black_two = np.zeros((400, 400), dtype=np.uint8)
black_two[200:300, 200:300] = 255

va operatoridan boshlaylik. Biz buni ikkita rasmimizga quyidagi tarzda cv.bitwise_andfunktsiyasiga o'tkazish orqali qo'llashimiz mumkin:

one_and_two = cv.bitwise_and(black_one, black_two)

Bit bo'yicha va amalning natijasi ikkala oq kvadratning kesishishidir. Ikki tasvirning kamida bittasida qora bo'lgan piksellar qora bo'lib qoladi.

Xuddi shunday, biz ikkita oq kvadrat orasidagi bog'lanishni topish uchun yokioperatordan foydalanishimiz mumkin: ikkita tasvirdan kamida bittasida piksel oq bo'lsa - ikkita operanddan kamida bittasi "to'g'ri" bo'lsa. ” — , natijada olingan tasvirda oq bo‘lib qoladi:

Xuddi shunday, biz ushbu amalni bajarish uchun bitwise_or funksiyasidan foydalandik.

Yoki inklyuziv, ya'ni ikkala rasmda oq bo'lgan piksellar oq bo'lib qoladi. Agar siz ikkala tasvirda ham oq rangga ega bo‘lmagan piksellarni tanlashni istasangiz, xor operatoridan foydalanasiz:

Ushbu transformatsiyani amalga oshiradigan funksiya cv.bitwise_xor va avvalgi ikkita funksiya bilan bir xil ishlaydi.

Nihoyat, cv.bitwise_not funksiyasi bitta tasvirni oladi va qora va oq piksellarni almashtiradi. Mana, black_one bo'lmagandan keyin qanday ko'rinishga ega.

Endi biz bitli operatorlarning ikkilik tasvirlarda qanday ishlashini bilganimizdan so'ng, uni BGR tasvirlariga o'tkazish oson. Bitli operatsiyalar har bir pikselda bir butun sifatida ishlash o'rniga kanallarga o'zgartirishlarni qo'llaydi. Keling, quyidagi kod yordamida yaratadigan "sof yashil" tasvirni ishlatib, misol keltiraylik:

green = np.zeros((400, 400, 3), dtype=np.uint8)
green[:, :, 1] = 255

Har bir piksel uchta kanaldan iborat bo'lib, ular mos ravishda ko'k, yashil va qizil ranglar miqdorini ifodalovchi 0 dan 255 gacha bo'lgan butun sonlardir. Shuning uchun, BGR tasvirlariga bitli operatsiyalarni qo'llash avvalgidek natijalarni beradi, lekin uchinchi o'lchovda.

Keling, notoperatoriga misol keltiramiz:

Yashil kanalni o'chirmaydi va qizil va ko'k kanallarni yoqadi.

Nooperatorning xatti-harakatini umumlashtirish uchun shuni aytishimiz mumkinki, piksellar 127 ga nisbatan simmetrik bilan almashtiriladi. Masalan, 0 255 ga, 200 esa 55 ga aylanadi.

OpenCV bitwise_xx funksiyalarini biz hali foydalanmagan qo'shimcha parametr bilan chaqirish mumkin: mask. Maskalar tasviringizning muayyan hududlarini tanlash imkonini beradi. Misol ming so'zga teng, shuning uchun rasmga va operatorini niqob sifatida ishlatib, black_one dan foydalanadi:

Mana bu natijani yaratgan kod:

image: np.ndarray = cv.imread(image_path)
mask = np.zeros(image.shape[:2], dtype=np.uint8)
mask[150:250, 150:250] = 255
masked_image = cv.bitwise_and(image, image, mask=mask)

Maskalardan foydalanishda ikkita narsani bilish kerak:

  • ular ikki o'lchovli bo'lishi kerak;
  • unga ikkilik chegara qo'llaniladi, ya'ni kulrang koeffitsienti 0 dan 127 gacha bo'lgan barcha piksellar 0 ga, quyuqroqlari esa 255 ga yaxlitlanadi.

Endi biz BGR tasviridan hududlarni olish uchun niqoblardan qanday foydalanishni bilganimizdan so'ng, bularning barchasini bitta yakuniy, aniqroq va biroz murakkabroq misol ustida ishlash uchun birlashtirish vaqti keldi. Bizning maqsadimiz quyidagi rasmni yaratishdir:

ushbu ikkita rasmdan:

E'tibor bering, ob-havo belgisi qattiq oq fonda, ya'ni biz avval quyosh va bulutni ajratib olishimiz kerak.

Quyidagi yechimni o‘qishdan oldin bu muammoni o‘zingiz hal qilishga ruxsat beraman…

Yechim: Biz qilishimiz kerak bo'lgan birinchi narsa ikkala rasmning shaklini hisoblashdir. Orqa fon va oldingi fonning oʻlchamlari har xil boʻlgani uchun biz birinchi rasmdan qiziqarli hududni(ROI)ni tanlashimiz kerak. ROI biz ishlayotgan tasvirning bir qismidir. Rasmning yuqori chap burchagidagi piktogramma o'lchamidagi to'rtburchakni tanlash uchun NumPy o'rnatilgan indekslashdan foydalanamiz. Ushbu qadam asl tasvir va piktogramma ustida bit bo'yicha operatsiyalarni bajarish uchun juda muhim, chunki ular barcha tasvirlar va niqob bir xil o'lchamlarga ega bo'lishini talab qiladi. Mana biz foydalanishimiz mumkin bo'lgan kod:

weather = cv.imread(weather_path)
weather_height, weather_width = weather.shape[:2]
region_of_interest = image[:weather_height, :weather_width]

Keyin ikonani ikki qismga ajratishimiz kerak: rangli, oldingi planda bo'ladi; va oq fon ko'l tasviri bilan almashtiriladi. Haqiqiy belgini fondan aniq ajratib olishimiz uchun rasmimizga ikkilik chegara qo'llashdan boshlaymiz:

greyscale_sun = cv.cvtColor(weather, cv.COLOR_BGR2GRAY)
_, threshold = cv.threshold(greyscale_sun, 254, 255, cv.THRESH_BINARY)

Bizga thresholdfunktsiyasi tomonidan qaytarilgan birinchi elementni saqlashimiz shart emas. Fon butunlay oq bo'lgani uchun biz pastki chegarani 254 ga qo'ydik. Bu to'liq oq bo'lmagan barcha piksellar qora rangga aylanadi degan ma'noni anglatadi. Bu erda olingan rasm:

Ushbu rasmni niqob sifatida ishlatib, biz endi ko'lning rasmini joylashtiramiz va piktogramma joylashtiriladigan bo'sh joy qoldiramiz:

background = cv.bitwise_and(region_of_interest, region_of_interest, mask=threshold)

Bu bizga quyidagi rasmni beradi:

Endi yuqoridagi rasmning qora qismiga mos keladigan belgining qismini tanlashimiz kerak. Buning uchun biz niqobning teskarisini olamiz va uni butun belgiga qo'llaymiz:

threshold_inverse = cv.bitwise_not(threshold)
icon = cv.bitwise_and(weather, weather, mask=threshold_inverse)

Mana belgi qanday ko'rinishga ega:

Biz OpenCV addfunktsiyasidan foydalanib, ushbu ikkita rasmni bir joyga qo'yishimiz mumkin, bu esa ushbu tasvirni beradi:

Oxirgi qadam asl rasmning yuqori chap burchagini tahrirlangan ROI bilan almashtirishdir:

image[:weather_height, :weather_width] = cv.add(background, icon)

va Bob sizning amakingiz, siz bu yoqimli kollajni olasiz!