Python ile Görüntü İşleme ve Computer Vision: Pillow ve OpenCV Kullanımı
Python Pillow ve OpenCV ile görüntü işleme rehberi. Resize, crop, filter uygulama, filigran ekleme, otomatik görsel optimizasyon teknikleri.
Görüntü işleme, modern yazılım geliştirmenin vazgeçilmez bir parçası haline gelmiştir. Web uygulamalarından mobil uygulamalara, makine öğrenmesinden veri görselleştirmeye kadar birçok alanda görüntü manipülasyonuna ihtiyaç duyarız. Python ekosistemi, Pillow ve OpenCV gibi güçlü kütüphaneler sayesinde bu ihtiyaçları karşılamak için mükemmel araçlar sunar.
Görüntü İşleme Nedir?
Görüntü işleme, dijital görüntüler üzerinde çeşitli operasyonlar gerçekleştirerek onları dönüştürme, analiz etme veya iyileştirme sürecidir. Bu süreç, basit yeniden boyutlandırmadan karmaşık makine öğrenmesi uygulamalarına kadar geniş bir yelpaze kapsar.
Görüntü İşleme Kullanım Alanları
- Web Uygulamaları: Kullanıcı profil fotoğrafları, ürün görselleri
- E-Ticaret: Ürün görsellerinin optimize edilmesi, filigran ekleme
- Sosyal Medya: Filtreler, efektler, otomatik kırpma
- Makine Öğrenmesi: Veri ön işleme, augmentation
- Belge İşleme: OCR, form tanıma, QR kod okuma
- Medikal Görüntüleme: Röntgen, MR analizi
Pillow ve OpenCV Karşılaştırması
Python görüntü işleme kütüphaneleri karşılaştırması Python ekosistemindeki popüler görüntü işleme araçları
Pillow (PIL Fork)
Avantajları:
- Basit ve kullanımı kolay API
- Hızlı temel operasyonlar
- Geniş format desteği (JPEG, PNG, GIF, BMP, TIFF vb.)
- Hafif ve bağımlılık az
- Web uygulamaları için ideal
Dezavantajları:
- Gelişmiş görüntü işleme özellikleri sınırlı
- Computer vision algoritmaları yok
- Video işleme desteği yok
OpenCV
Avantajları:
- Gelişmiş computer vision algoritmaları
- Yüz tanıma, nesne tespiti
- Video işleme desteği
- Yüksek performans (C++ backend)
- Makine öğrenmesi entegrasyonu
Dezavantajları:
- Daha karmaşık API
- Büyük kütüphane boyutu
- Basit işlemler için overkill olabilir
Pillow ile Temel Görüntü İşleme
Kurulum ve İlk Adımlar
1
2
3
4
5
6
# Pillow kurulumu
pip install Pillow
# İsteğe bağlı: İlave format desteği için
pip install pillow-heif # HEIF/HEIC format
pip install pillow-avif # AVIF format
Görüntü Yükleme ve Kaydetme
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
56
57
58
59
60
61
62
63
64
65
66
67
from PIL import Image
import os
class ImageProcessor:
"""Temel görüntü işleme sınıfı"""
def __init__(self, image_path: str):
"""
Görüntü yükleme
Args:
image_path: Görüntü dosya yolu
"""
self.image = Image.open(image_path)
self.original = self.image.copy() # Orijinali sakla
# Görüntü bilgileri
print(f"Format: {self.image.format}")
print(f"Boyut: {self.image.size}") # (width, height)
print(f"Mod: {self.image.mode}") # RGB, RGBA, L (grayscale)
def save(self, output_path: str, quality: int = 85, optimize: bool = True):
"""
Görüntüyü kaydetme
Args:
output_path: Çıktı dosya yolu
quality: JPEG kalitesi (1-95)
optimize: Dosya boyutu optimizasyonu
"""
# Format otomatik tespit (uzantıdan)
ext = os.path.splitext(output_path)[1].lower()
# JPEG için özel parametreler
if ext in ['.jpg', '.jpeg']:
self.image.save(
output_path,
'JPEG',
quality=quality,
optimize=optimize,
progressive=True # Progressive JPEG
)
# PNG için optimizasyon
elif ext == '.png':
self.image.save(
output_path,
'PNG',
optimize=optimize,
compress_level=9 # Maksimum sıkıştırma
)
else:
self.image.save(output_path, optimize=optimize)
print(f"Saved: {output_path}")
# Dosya boyutu bilgisi
file_size = os.path.getsize(output_path) / 1024 # KB
print(f"File size: {file_size:.2f} KB")
def reset(self):
"""Orijinal görüntüye geri dön"""
self.image = self.original.copy()
# Kullanım örneği
processor = ImageProcessor('input.jpg')
processor.save('output.jpg', quality=90)
Görüntü Boyutlandırma
Görüntü boyutlandırma, kırpma ve filtre uygulama Görüntü üzerinde temel dönüşüm operasyonları
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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
from PIL import Image
from typing import Tuple, Optional
def resize_image(
image: Image.Image,
size: Tuple[int, int],
maintain_aspect: bool = True,
resample: int = Image.Resampling.LANCZOS
) -> Image.Image:
"""
Görüntü boyutlandırma
Args:
image: PIL Image objesi
size: Hedef boyut (width, height)
maintain_aspect: En-boy oranını koru
resample: Resampling algoritması
Returns:
Boyutlandırılmış görüntü
"""
if maintain_aspect:
# En-boy oranını koruyarak boyutlandır
image.thumbnail(size, resample)
return image
else:
# Tam boyuta zorlama (distortion olabilir)
return image.resize(size, resample)
def resize_to_width(image: Image.Image, width: int) -> Image.Image:
"""Genişliği belirle, yüksekliği oranla hesapla"""
aspect_ratio = image.height / image.width
height = int(width * aspect_ratio)
return image.resize((width, height), Image.Resampling.LANCZOS)
def resize_to_height(image: Image.Image, height: int) -> Image.Image:
"""Yüksekliği belirle, genişliği oranla hesapla"""
aspect_ratio = image.width / image.height
width = int(height * aspect_ratio)
return image.resize((width, height), Image.Resampling.LANCZOS)
def smart_resize(
image: Image.Image,
max_width: int,
max_height: int
) -> Image.Image:
"""
Maksimum boyutları aşmadan akıllı boyutlandırma
"""
width, height = image.size
# Eğer görüntü zaten küçükse, dokunma
if width <= max_width and height <= max_height:
return image
# En-boy oranını koruyarak boyutlandır
width_ratio = max_width / width
height_ratio = max_height / height
# En küçük oranı kullan (her iki boyut da sınırlar içinde kalır)
ratio = min(width_ratio, height_ratio)
new_width = int(width * ratio)
new_height = int(height * ratio)
return image.resize((new_width, new_height), Image.Resampling.LANCZOS)
# Kullanım örnekleri
img = Image.open('photo.jpg')
# Basit boyutlandırma
resized = resize_image(img, (800, 600), maintain_aspect=True)
# Genişlik bazlı
width_based = resize_to_width(img, 1200)
# Maksimum boyut sınırı ile
smart = smart_resize(img, max_width=1920, max_height=1080)
# Thumbnail oluşturma (orijinali değiştirir!)
img_copy = img.copy()
img_copy.thumbnail((200, 200), Image.Resampling.LANCZOS)
Görüntü Kırpma (Cropping)
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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
from PIL import Image
from typing import Tuple
def crop_center(image: Image.Image, crop_width: int, crop_height: int) -> Image.Image:
"""
Merkezi kırpma
Args:
image: PIL Image objesi
crop_width: Kırpma genişliği
crop_height: Kırpma yüksekliği
Returns:
Kırpılmış görüntü
"""
width, height = image.size
# Merkez koordinatları
left = (width - crop_width) // 2
top = (height - crop_height) // 2
right = left + crop_width
bottom = top + crop_height
# Kırpma (left, top, right, bottom)
return image.crop((left, top, right, bottom))
def crop_to_aspect_ratio(
image: Image.Image,
aspect_ratio: float
) -> Image.Image:
"""
Belirli bir en-boy oranına kırpma
Args:
image: PIL Image objesi
aspect_ratio: Hedef en-boy oranı (width/height)
Örn: 16/9 = 1.778, 4/3 = 1.333, 1/1 = 1.0
"""
width, height = image.size
current_ratio = width / height
if current_ratio > aspect_ratio:
# Görüntü çok geniş, kenarlardan kes
new_width = int(height * aspect_ratio)
left = (width - new_width) // 2
return image.crop((left, 0, left + new_width, height))
else:
# Görüntü çok yüksek, üst/alttan kes
new_height = int(width / aspect_ratio)
top = (height - new_height) // 2
return image.crop((0, top, width, top + new_height))
def smart_crop_face_detection(image_path: str, output_size: Tuple[int, int]) -> Image.Image:
"""
Yüz tespiti ile akıllı kırpma (OpenCV gerektirir)
Not: Bu fonksiyon Pillow ve OpenCV'yi birlikte kullanır
"""
import cv2
import numpy as np
# Görüntüyü yükle
img = Image.open(image_path)
cv_image = cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR)
# Yüz tespiti için Haar Cascade
face_cascade = cv2.CascadeClassifier(
cv2.data.haarcascades + 'haarcascade_frontalface_default.xml'
)
# Gri tonlamaya çevir (yüz tespiti için)
gray = cv2.cvtColor(cv_image, cv2.COLOR_BGR2GRAY)
# Yüzleri tespit et
faces = face_cascade.detectMultiScale(gray, 1.1, 4)
if len(faces) > 0:
# İlk yüzü merkez al
x, y, w, h = faces[0]
# Yüz etrafında kırpma alanı hesapla
center_x = x + w // 2
center_y = y + h // 2
crop_w, crop_h = output_size
left = max(0, center_x - crop_w // 2)
top = max(0, center_y - crop_h // 2)
right = min(img.width, left + crop_w)
bottom = min(img.height, top + crop_h)
return img.crop((left, top, right, bottom))
else:
# Yüz bulunamazsa merkezi kırp
return crop_center(img, *output_size)
# Kullanım örnekleri
img = Image.open('landscape.jpg')
# Merkezi kırpma
cropped = crop_center(img, 800, 600)
# 16:9 en-boy oranına kırpma
wide_screen = crop_to_aspect_ratio(img, 16/9)
# Kare kırpma (1:1)
square = crop_to_aspect_ratio(img, 1.0)
# Yüz tespiti ile kırpma
face_cropped = smart_crop_face_detection('portrait.jpg', (400, 400))
Görüntü Filtreleri ve Efektler
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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
from PIL import Image, ImageFilter, ImageEnhance
from typing import Optional
class ImageEffects:
"""Görüntü efektleri ve filtreleri"""
@staticmethod
def apply_blur(image: Image.Image, radius: int = 2) -> Image.Image:
"""Bulanıklaştırma efekti"""
return image.filter(ImageFilter.GaussianBlur(radius))
@staticmethod
def apply_sharpen(image: Image.Image, factor: float = 1.5) -> Image.Image:
"""Keskinleştirme efekti"""
enhancer = ImageEnhance.Sharpness(image)
return enhancer.enhance(factor)
@staticmethod
def apply_edge_enhance(image: Image.Image) -> Image.Image:
"""Kenar belirginleştirme"""
return image.filter(ImageFilter.EDGE_ENHANCE_MORE)
@staticmethod
def apply_emboss(image: Image.Image) -> Image.Image:
"""Kabartma efekti"""
return image.filter(ImageFilter.EMBOSS)
@staticmethod
def adjust_brightness(image: Image.Image, factor: float = 1.2) -> Image.Image:
"""
Parlaklık ayarlama
Args:
factor: 1.0 = orijinal, <1.0 = koyulaştır, >1.0 = aydınlat
"""
enhancer = ImageEnhance.Brightness(image)
return enhancer.enhance(factor)
@staticmethod
def adjust_contrast(image: Image.Image, factor: float = 1.2) -> Image.Image:
"""
Kontrast ayarlama
Args:
factor: 1.0 = orijinal, <1.0 = azalt, >1.0 = arttır
"""
enhancer = ImageEnhance.Contrast(image)
return enhancer.enhance(factor)
@staticmethod
def adjust_saturation(image: Image.Image, factor: float = 1.3) -> Image.Image:
"""
Renk doygunluğu ayarlama
Args:
factor: 1.0 = orijinal, 0.0 = siyah-beyaz, >1.0 = daha canlı
"""
enhancer = ImageEnhance.Color(image)
return enhancer.enhance(factor)
@staticmethod
def convert_to_grayscale(image: Image.Image) -> Image.Image:
"""Siyah-beyaz dönüşümü"""
return image.convert('L')
@staticmethod
def apply_sepia(image: Image.Image) -> Image.Image:
"""Sepia (nostaljik) efekti"""
# RGB moduna çevir
img = image.convert('RGB')
pixels = img.load()
for i in range(img.width):
for j in range(img.height):
r, g, b = pixels[i, j]
# Sepia formülü
tr = int(0.393 * r + 0.769 * g + 0.189 * b)
tg = int(0.349 * r + 0.686 * g + 0.168 * b)
tb = int(0.272 * r + 0.534 * g + 0.131 * b)
# 255 sınırını aşma kontrolü
pixels[i, j] = (min(tr, 255), min(tg, 255), min(tb, 255))
return img
@staticmethod
def create_thumbnail_with_border(
image: Image.Image,
size: tuple,
border_size: int = 5,
border_color: str = 'white'
) -> Image.Image:
"""Kenarlıklı thumbnail oluşturma"""
# Thumbnail oluştur
img = image.copy()
img.thumbnail(size, Image.Resampling.LANCZOS)
# Kenarlık ekle
bordered = Image.new(
'RGB',
(img.width + border_size * 2, img.height + border_size * 2),
border_color
)
bordered.paste(img, (border_size, border_size))
return bordered
# Kullanım örnekleri
img = Image.open('photo.jpg')
# Filtreler
blurred = ImageEffects.apply_blur(img, radius=5)
sharpened = ImageEffects.apply_sharpen(img, factor=2.0)
embossed = ImageEffects.apply_emboss(img)
# Renk ayarlamaları
brightened = ImageEffects.adjust_brightness(img, 1.3)
high_contrast = ImageEffects.adjust_contrast(img, 1.5)
vibrant = ImageEffects.adjust_saturation(img, 1.4)
# Dönüşümler
grayscale = ImageEffects.convert_to_grayscale(img)
sepia = ImageEffects.apply_sepia(img)
# Kenarlıklı thumbnail
thumbnail = ImageEffects.create_thumbnail_with_border(img, (300, 300))
Filigran (Watermark) Ekleme
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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
from PIL import Image, ImageDraw, ImageFont
from typing import Tuple, Optional
def add_text_watermark(
image: Image.Image,
text: str,
position: str = 'bottom-right',
font_size: int = 36,
opacity: int = 128,
margin: int = 20
) -> Image.Image:
"""
Metin filigranı ekleme
Args:
image: PIL Image objesi
text: Filigran metni
position: Konum ('bottom-right', 'bottom-left', 'top-right', 'top-left', 'center')
font_size: Font boyutu
opacity: Opaklık (0-255)
margin: Kenar boşluğu
"""
# RGBA moduna çevir (transparanlık için)
img = image.convert('RGBA')
# Transparent katman oluştur
txt_layer = Image.new('RGBA', img.size, (255, 255, 255, 0))
draw = ImageDraw.Draw(txt_layer)
# Font yükleme (varsayılan font kullanımı)
try:
font = ImageFont.truetype('/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf', font_size)
except:
font = ImageFont.load_default()
# Metin boyutunu hesapla
bbox = draw.textbbox((0, 0), text, font=font)
text_width = bbox[2] - bbox[0]
text_height = bbox[3] - bbox[1]
# Pozisyon hesaplama
if position == 'bottom-right':
x = img.width - text_width - margin
y = img.height - text_height - margin
elif position == 'bottom-left':
x = margin
y = img.height - text_height - margin
elif position == 'top-right':
x = img.width - text_width - margin
y = margin
elif position == 'top-left':
x = margin
y = margin
elif position == 'center':
x = (img.width - text_width) // 2
y = (img.height - text_height) // 2
else:
x, y = margin, margin
# Metni çiz (beyaz renk, ayarlı opacity)
draw.text((x, y), text, fill=(255, 255, 255, opacity), font=font)
# Katmanları birleştir
watermarked = Image.alpha_composite(img, txt_layer)
# RGB'ye geri dön
return watermarked.convert('RGB')
def add_image_watermark(
image: Image.Image,
watermark_path: str,
position: str = 'bottom-right',
scale: float = 0.1,
opacity: int = 128,
margin: int = 20
) -> Image.Image:
"""
Görüntü filigranı ekleme
Args:
image: Ana görüntü
watermark_path: Filigran görüntü yolu
position: Konum
scale: Filigran boyutu (ana görüntünün yüzdesi)
opacity: Opaklık
margin: Kenar boşluğu
"""
# Ana görüntüyü RGBA'ya çevir
base = image.convert('RGBA')
# Filigran yükle
watermark = Image.open(watermark_path).convert('RGBA')
# Filigran boyutunu ayarla
wm_width = int(base.width * scale)
wm_height = int(watermark.height * (wm_width / watermark.width))
watermark = watermark.resize((wm_width, wm_height), Image.Resampling.LANCZOS)
# Opacity ayarla
alpha = watermark.split()[3]
alpha = alpha.point(lambda p: int(p * (opacity / 255)))
watermark.putalpha(alpha)
# Pozisyon hesapla
if position == 'bottom-right':
x = base.width - watermark.width - margin
y = base.height - watermark.height - margin
elif position == 'bottom-left':
x = margin
y = base.height - watermark.height - margin
elif position == 'top-right':
x = base.width - watermark.width - margin
y = margin
elif position == 'top-left':
x = margin
y = margin
elif position == 'center':
x = (base.width - watermark.width) // 2
y = (base.height - watermark.height) // 2
else:
x, y = margin, margin
# Filigranı yapıştır
base.paste(watermark, (x, y), watermark)
return base.convert('RGB')
# Kullanım örnekleri
img = Image.open('photo.jpg')
# Metin filigranı
watermarked_text = add_text_watermark(
img,
text='© 2025 My Company',
position='bottom-right',
font_size=40,
opacity=150
)
# Görüntü filigranı
watermarked_image = add_image_watermark(
img,
watermark_path='logo.png',
position='bottom-right',
scale=0.15,
opacity=180
)
OpenCV ile Gelişmiş Görüntü İşleme
Python ile görsel manipülasyon yöntemleri OpenCV ile gelişmiş görüntü işleme teknikleri
OpenCV Kurulumu ve Temel Kullanım
1
2
3
4
5
6
7
8
# OpenCV kurulumu
pip install opencv-python
# Ekstra modüller (opsiyonel)
pip install opencv-contrib-python
# NumPy (gerekli)
pip install numpy
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
56
57
58
import cv2
import numpy as np
from typing import Tuple
class OpenCVProcessor:
"""OpenCV ile görüntü işleme"""
def __init__(self, image_path: str):
"""Görüntü yükleme"""
# BGR formatında yükler (not RGB!)
self.image = cv2.imread(image_path)
if self.image is None:
raise ValueError(f"Could not load image: {image_path}")
print(f"Shape: {self.image.shape}") # (height, width, channels)
print(f"Size: {self.image.size}") # total pixels
print(f"Dtype: {self.image.dtype}") # uint8
def show(self, window_name: str = 'Image'):
"""Görüntüyü göster"""
cv2.imshow(window_name, self.image)
cv2.waitKey(0)
cv2.destroyAllWindows()
def save(self, output_path: str, quality: int = 95):
"""Görüntüyü kaydet"""
if output_path.endswith('.jpg') or output_path.endswith('.jpeg'):
cv2.imwrite(
output_path,
self.image,
[cv2.IMWRITE_JPEG_QUALITY, quality]
)
elif output_path.endswith('.png'):
cv2.imwrite(
output_path,
self.image,
[cv2.IMWRITE_PNG_COMPRESSION, 9]
)
else:
cv2.imwrite(output_path, self.image)
print(f"Saved: {output_path}")
def to_rgb(self):
"""BGR'den RGB'ye dönüşüm"""
self.image = cv2.cvtColor(self.image, cv2.COLOR_BGR2RGB)
return self
def to_grayscale(self):
"""Gri tonlamaya dönüşüm"""
self.image = cv2.cvtColor(self.image, cv2.COLOR_BGR2GRAY)
return self
# Kullanım
processor = OpenCVProcessor('photo.jpg')
processor.to_grayscale().save('gray.jpg')
Renk Uzayı Dönüşümleri
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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
import cv2
import numpy as np
def convert_color_space(image: np.ndarray, conversion: str) -> np.ndarray:
"""
Renk uzayı dönüşümleri
Args:
image: NumPy array (OpenCV formatı)
conversion: 'rgb', 'hsv', 'lab', 'gray', 'yuv'
"""
conversions = {
'rgb': cv2.COLOR_BGR2RGB,
'hsv': cv2.COLOR_BGR2HSV,
'lab': cv2.COLOR_BGR2LAB,
'gray': cv2.COLOR_BGR2GRAY,
'yuv': cv2.COLOR_BGR2YUV
}
if conversion not in conversions:
raise ValueError(f"Unknown conversion: {conversion}")
return cv2.cvtColor(image, conversions[conversion])
def adjust_color_temperature(image: np.ndarray, temperature: float) -> np.ndarray:
"""
Renk sıcaklığı ayarlama
Args:
temperature: -1.0 (soğuk/mavi) ile 1.0 (sıcak/sarı) arası
"""
# LAB renk uzayına çevir
lab = cv2.cvtColor(image, cv2.COLOR_BGR2LAB)
l, a, b = cv2.split(lab)
# B kanalını ayarla (mavi-sarı)
b = b.astype(np.float32)
b += temperature * 50
b = np.clip(b, 0, 255).astype(np.uint8)
# Kanallları birleştir ve BGR'ye dön
lab = cv2.merge([l, a, b])
return cv2.cvtColor(lab, cv2.COLOR_LAB2BGR)
def auto_white_balance(image: np.ndarray) -> np.ndarray:
"""Otomatik beyaz dengesi"""
result = cv2.cvtColor(image, cv2.COLOR_BGR2LAB)
avg_a = np.average(result[:, :, 1])
avg_b = np.average(result[:, :, 2])
result[:, :, 1] = result[:, :, 1] - ((avg_a - 128) * (result[:, :, 0] / 255.0) * 1.1)
result[:, :, 2] = result[:, :, 2] - ((avg_b - 128) * (result[:, :, 0] / 255.0) * 1.1)
return cv2.cvtColor(result, cv2.COLOR_LAB2BGR)
# Kullanım
img = cv2.imread('photo.jpg')
# HSV'ye çevir
hsv = convert_color_space(img, 'hsv')
# Sıcak ton
warm = adjust_color_temperature(img, 0.5)
# Otomatik beyaz dengesi
balanced = auto_white_balance(img)
Kenar Tespiti ve Kontur Bulma
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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
import cv2
import numpy as np
from typing import List, Tuple
def detect_edges_canny(
image: np.ndarray,
threshold1: int = 100,
threshold2: int = 200
) -> np.ndarray:
"""
Canny kenar tespiti
Args:
threshold1: Alt eşik
threshold2: Üst eşik
"""
# Gri tonlamaya çevir
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# Gürültü azaltma
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
# Canny kenar tespiti
edges = cv2.Canny(blurred, threshold1, threshold2)
return edges
def find_contours(image: np.ndarray) -> List[np.ndarray]:
"""
Kontur bulma
Returns:
Kontur listesi
"""
# Gri tonlama ve eşikleme
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
_, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
# Konturları bul
contours, hierarchy = cv2.findContours(
binary,
cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE
)
return contours
def draw_contours(
image: np.ndarray,
contours: List[np.ndarray],
color: Tuple[int, int, int] = (0, 255, 0),
thickness: int = 2
) -> np.ndarray:
"""Konturları çiz"""
result = image.copy()
cv2.drawContours(result, contours, -1, color, thickness)
return result
def detect_shapes(image: np.ndarray) -> dict:
"""
Temel şekil tespiti
Returns:
Tespit edilen şekillerin sayıları
"""
contours = find_contours(image)
shapes = {'triangle': 0, 'rectangle': 0, 'circle': 0, 'other': 0}
for contour in contours:
# Konturu yaklaşık poligona dönüştür
perimeter = cv2.arcLength(contour, True)
approx = cv2.approxPolyDP(contour, 0.04 * perimeter, True)
# Köşe sayısına göre şekil belirle
vertices = len(approx)
if vertices == 3:
shapes['triangle'] += 1
elif vertices == 4:
shapes['rectangle'] += 1
elif vertices > 4:
shapes['circle'] += 1
else:
shapes['other'] += 1
return shapes
# Kullanım
img = cv2.imread('objects.jpg')
# Kenar tespiti
edges = detect_edges_canny(img)
# Kontur bulma ve çizme
contours = find_contours(img)
with_contours = draw_contours(img, contours)
# Şekil tespiti
shapes = detect_shapes(img)
print(f"Detected shapes: {shapes}")
Yüz Tespiti
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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
import cv2
import numpy as np
from typing import List, Tuple
def detect_faces(
image_path: str,
scaleFactor: float = 1.1,
minNeighbors: int = 5
) -> Tuple[np.ndarray, List[Tuple[int, int, int, int]]]:
"""
Haar Cascade ile yüz tespiti
Args:
image_path: Görüntü yolu
scaleFactor: Ölçeklendirme faktörü
minNeighbors: Minimum komşu sayısı
Returns:
(görüntü, yüz koordinatları listesi)
"""
# Görüntüyü yükle
img = cv2.imread(image_path)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Haar Cascade yükle
face_cascade = cv2.CascadeClassifier(
cv2.data.haarcascades + 'haarcascade_frontalface_default.xml'
)
# Yüzleri tespit et
faces = face_cascade.detectMultiScale(
gray,
scaleFactor=scaleFactor,
minNeighbors=minNeighbors,
minSize=(30, 30)
)
return img, faces
def draw_face_boxes(
image: np.ndarray,
faces: List[Tuple[int, int, int, int]],
color: Tuple[int, int, int] = (0, 255, 0),
thickness: int = 2
) -> np.ndarray:
"""Yüz etrafına kutu çiz"""
result = image.copy()
for (x, y, w, h) in faces:
cv2.rectangle(result, (x, y), (x+w, y+h), color, thickness)
# Yüz numarası ekle
cv2.putText(
result,
'Face',
(x, y-10),
cv2.FONT_HERSHEY_SIMPLEX,
0.5,
color,
thickness
)
return result
def blur_faces(
image: np.ndarray,
faces: List[Tuple[int, int, int, int]],
blur_amount: int = 25
) -> np.ndarray:
"""Yüzleri bulanıklaştır (gizlilik için)"""
result = image.copy()
for (x, y, w, h) in faces:
# Yüz bölgesini al
face_region = result[y:y+h, x:x+w]
# Bulanıklaştır
blurred_face = cv2.GaussianBlur(face_region, (blur_amount, blur_amount), 30)
# Geri yerleştir
result[y:y+h, x:x+w] = blurred_face
return result
def extract_faces(
image: np.ndarray,
faces: List[Tuple[int, int, int, int]],
padding: int = 20
) -> List[np.ndarray]:
"""Tespit edilen yüzleri ayrı görüntüler olarak çıkar"""
face_images = []
for (x, y, w, h) in faces:
# Padding ekle
x_start = max(0, x - padding)
y_start = max(0, y - padding)
x_end = min(image.shape[1], x + w + padding)
y_end = min(image.shape[0], y + h + padding)
# Yüz bölgesini kes
face_img = image[y_start:y_end, x_start:x_end]
face_images.append(face_img)
return face_images
# Kullanım örnekleri
img, faces = detect_faces('group_photo.jpg')
print(f"Detected {len(faces)} faces")
# Yüz etrafına kutu çiz
with_boxes = draw_face_boxes(img, faces)
cv2.imwrite('faces_detected.jpg', with_boxes)
# Yüzleri bulanıklaştır
blurred = blur_faces(img, faces, blur_amount=35)
cv2.imwrite('faces_blurred.jpg', blurred)
# Yüzleri çıkar ve kaydet
face_images = extract_faces(img, faces)
for i, face in enumerate(face_images):
cv2.imwrite(f'face_{i}.jpg', face)
Toplu Görüntü İşleme
Web uygulamalarında ve otomasyonlarda sıkça ihtiyaç duyulan toplu işleme örnekleri:
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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
import os
from pathlib import Path
from PIL import Image
from typing import List, Callable
from concurrent.futures import ThreadPoolExecutor, as_completed
from tqdm import tqdm
class BatchImageProcessor:
"""Toplu görüntü işleme sınıfı"""
def __init__(self, input_dir: str, output_dir: str):
self.input_dir = Path(input_dir)
self.output_dir = Path(output_dir)
# Çıktı dizinini oluştur
self.output_dir.mkdir(parents=True, exist_ok=True)
# Desteklenen formatlar
self.supported_formats = {'.jpg', '.jpeg', '.png', '.gif', '.bmp', '.tiff'}
def get_image_files(self) -> List[Path]:
"""Dizindeki tüm görüntü dosyalarını bul"""
image_files = []
for file_path in self.input_dir.rglob('*'):
if file_path.suffix.lower() in self.supported_formats:
image_files.append(file_path)
return image_files
def process_single(
self,
file_path: Path,
process_func: Callable,
**kwargs
) -> bool:
"""Tek bir görüntüyü işle"""
try:
# Görüntüyü yükle
img = Image.open(file_path)
# İşleme fonksiyonunu uygula
processed = process_func(img, **kwargs)
# Çıktı yolunu hesapla (yapıyı koru)
relative_path = file_path.relative_to(self.input_dir)
output_path = self.output_dir / relative_path
# Çıktı dizinini oluştur
output_path.parent.mkdir(parents=True, exist_ok=True)
# Kaydet
processed.save(output_path, quality=90, optimize=True)
return True
except Exception as e:
print(f"Error processing {file_path}: {e}")
return False
def process_batch(
self,
process_func: Callable,
max_workers: int = 4,
**kwargs
) -> dict:
"""
Toplu işleme (paralel)
Args:
process_func: İşleme fonksiyonu (image -> processed_image)
max_workers: Maksimum thread sayısı
**kwargs: İşleme fonksiyonuna geçilecek parametreler
Returns:
İşlem istatistikleri
"""
image_files = self.get_image_files()
total = len(image_files)
print(f"Found {total} images to process")
success_count = 0
failed_count = 0
# ThreadPoolExecutor ile paralel işleme
with ThreadPoolExecutor(max_workers=max_workers) as executor:
# Future'ları başlat
futures = {
executor.submit(self.process_single, file_path, process_func, **kwargs): file_path
for file_path in image_files
}
# Progress bar ile takip et
with tqdm(total=total, desc="Processing") as pbar:
for future in as_completed(futures):
if future.result():
success_count += 1
else:
failed_count += 1
pbar.update(1)
return {
'total': total,
'success': success_count,
'failed': failed_count
}
# İşleme fonksiyonları tanımla
def resize_for_web(image: Image.Image, max_width: int = 1200) -> Image.Image:
"""Web için optimize boyutlandırma"""
if image.width > max_width:
aspect = image.height / image.width
new_height = int(max_width * aspect)
return image.resize((max_width, new_height), Image.Resampling.LANCZOS)
return image
def create_thumbnail(image: Image.Image, size: tuple = (300, 300)) -> Image.Image:
"""Thumbnail oluştur"""
img = image.copy()
img.thumbnail(size, Image.Resampling.LANCZOS)
return img
def add_company_watermark(image: Image.Image, opacity: int = 128) -> Image.Image:
"""Şirket filigranı ekle"""
from PIL import ImageDraw, ImageFont
img = image.convert('RGBA')
txt_layer = Image.new('RGBA', img.size, (255, 255, 255, 0))
draw = ImageDraw.Draw(txt_layer)
text = "© 2025 Company"
font_size = int(img.width * 0.03)
try:
font = ImageFont.truetype('/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf', font_size)
except:
font = ImageFont.load_default()
bbox = draw.textbbox((0, 0), text, font=font)
text_width = bbox[2] - bbox[0]
text_height = bbox[3] - bbox[1]
x = img.width - text_width - 20
y = img.height - text_height - 20
draw.text((x, y), text, fill=(255, 255, 255, opacity), font=font)
watermarked = Image.alpha_composite(img, txt_layer)
return watermarked.convert('RGB')
# Kullanım örnekleri
# Web için boyutlandırma
processor = BatchImageProcessor('raw_photos/', 'web_optimized/')
stats = processor.process_batch(resize_for_web, max_width=1920, max_workers=8)
print(f"Processed: {stats['success']}/{stats['total']}")
# Thumbnail oluşturma
thumb_processor = BatchImageProcessor('products/', 'thumbnails/')
thumb_processor.process_batch(create_thumbnail, size=(400, 400))
# Filigran ekleme
watermark_processor = BatchImageProcessor('originals/', 'watermarked/')
watermark_processor.process_batch(add_company_watermark, opacity=150)
Best Practices ve Performans İpuçları
1. Bellek Yönetimi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from PIL import Image
import gc
def process_large_images(image_paths: list):
"""Büyük görüntülerle çalışırken bellek yönetimi"""
for path in image_paths:
# Görüntüyü yükle
img = Image.open(path)
# İşlemleri yap
processed = img.resize((800, 600), Image.Resampling.LANCZOS)
processed.save(f'processed_{path}')
# Bellekten temizle
img.close()
processed.close()
# Garbage collection zorla (büyük dosyalar için)
gc.collect()
2. Lazy Loading
1
2
3
4
5
6
7
8
9
10
11
from PIL import Image
# ❌ YANLIŞ: Tüm görüntü belleğe yüklenir
img = Image.open('huge_image.jpg')
width, height = img.size
# DOĞRU: Sadece metadata okunur
with Image.open('huge_image.jpg') as img:
width, height = img.size
format = img.format
# Görüntü verisi henüz yüklenmedi
3. Format Seçimi
1
2
3
4
5
6
7
8
9
def optimize_image_format(image: Image.Image, has_transparency: bool = False):
"""Uygun format seçimi"""
if has_transparency:
# Transparanlık varsa PNG kullan
image.save('output.png', 'PNG', optimize=True, compress_level=9)
else:
# Transparanlık yoksa JPEG daha iyi
image.save('output.jpg', 'JPEG', quality=85, optimize=True)
4. Thumbnail Strategy
1
2
3
4
5
6
7
8
9
10
from PIL import Image
# ❌ YANLIŞ: Büyük görüntüyü yükleyip küçült
img = Image.open('huge.jpg') # 20MB
img.thumbnail((200, 200))
# DOĞRU: Draft mode kullan
img = Image.open('huge.jpg')
img.draft('RGB', (200, 200)) # Hızlı ve hafif
img.thumbnail((200, 200))
Sonuç
Python ile görüntü işleme, Pillow ve OpenCV kütüphaneleri sayesinde hem basit hem de gelişmiş seviyede mümkündür. Pillow, web uygulamaları ve temel manipülasyonlar için ideal bir seçimken, OpenCV computer vision ve gelişmiş analiz gerektiren projelerde tercih edilmelidir.
Bu yazıda ele aldığımız konular:
- Pillow Temel İşlemler: Yükleme, kaydetme, boyutlandırma, kırpma
- Görüntü Filtreleri: Blur, sharpen, brightness, contrast ayarları
- Filigran Ekleme: Metin ve görüntü watermark’ları
- OpenCV İleri Seviye: Renk uzayları, kenar tespiti, yüz tanıma
- Toplu İşleme: Paralel işleme, optimizasyon stratejileri
- Best Practices: Bellek yönetimi, format seçimi, performans
Görüntü işleme projeleri geliştirirken dikkat edilmesi gereken en önemli noktalar performans, bellek kullanımı ve dosya formatı seçimidir. Production ortamında mutlaka error handling, logging ve monitoring implementasyonu yapın.
