OpenCV چیست؟ آموزش کامل پردازش تصویر و ویدئو با پایتون
در این آموزش جامع OpenCV، نصب کتابخانه، خواندن و ویرایش تصویر، رنگ، فیلتر، Threshold، تشخیص لبه و کانتور، پردازش ویدئو، ساخت اسکنر سند و اتصال تصویر به هوش مصنوعی درواره را عملی یاد میگیرید.
بینایی ماشین یا Computer Vision یکی از مهمترین حوزههای هوش مصنوعی است. در این حوزه تلاش میکنیم کامپیوتر بتواند تصویر و ویدئو را پردازش کند، اجزای آن را تشخیص دهد، اطلاعات بصری را استخراج کند و براساس آن خروجی ساختاریافته تولید کند.
OpenCV یکی از شناختهشدهترین کتابخانههای متنباز برای پردازش تصویر و ویدئو است. این کتابخانه ابزارهایی برای خواندن تصویر، تغییر اندازه، تبدیل رنگ، کاهش نویز، تشخیص لبه، استخراج کانتور، پردازش ویدئو و اجرای مدلهای بینایی ماشین ارائه میدهد.
در این مقاله یاد میگیرید:
- OpenCV چیست و چه کاربردهایی دارد
- چگونه OpenCV را در پایتون نصب کنید
- تفاوت پکیجهای مختلف OpenCV چیست
- تصویر در OpenCV چگونه ذخیره میشود
- چگونه تصویر را بخوانید، نمایش دهید و ذخیره کنید
- چگونه Crop، Resize، Rotate و Flip انجام دهید
- چگونه فضای رنگ را تغییر دهید
- چگونه Blur، Threshold و Morphology اعمال کنید
- چگونه لبه و Contour اشیا را استخراج کنید
- چگونه ویدئو و وبکم را پردازش کنید
- چگونه یک اسکنر ساده سند بسازید
- چگونه تصویر را برای تحلیل به API هوش مصنوعی درواره بفرستید
OpenCV چیست؟
OpenCV مخفف Open Source Computer Vision Library است. این کتابخانه مجموعه بزرگی از الگوریتمها و ابزارهای پردازش تصویر، ویدئو و بینایی ماشین را ارائه میکند.
OpenCV در اصل با C++ توسعه داده شده، اما Binding رسمی پایتون آن باعث میشود بتوانیم بسیاری از قابلیتهای کتابخانه را با کد پایتون استفاده کنیم.
طبق مستندات رسمی OpenCV، این کتابخانه ماژولهایی برای پردازش تصویر، تحلیل ویدئو، کالیبراسیون دوربین، استخراج Feature، Machine Learning و اجرای شبکههای عصبی دارد.
کاربردهای رایج OpenCV عبارتاند از:
- تغییر اندازه و فرمت تصویر
- Crop خودکار تصویر
- بهبود اولیه کیفیت تصویر
- کاهش Noise
- تشخیص لبه و شکل
- شمارش اشیای ساده
- خواندن فریمهای ویدئو
- استخراج Thumbnail از ویدئو
- آمادهسازی ورودی مدلهای هوش مصنوعی
- ساخت اسکنر سند
- تشخیص ناحیه متن پیش از OCR
- پردازش تصویر محصول
- تشخیص تغییر میان دو تصویر
- ساخت Dataset بینایی ماشین
- رسم Bounding Box و Label
تفاوت پردازش تصویر و بینایی ماشین
این دو عبارت مرتبطاند، اما دقیقاً یکسان نیستند.
پردازش تصویر
در Image Processing معمولاً ورودی و خروجی هر دو تصویر هستند.
برای مثال:
- کاهش Noise
- افزایش Contrast
- Resize
- Threshold
- Sharpen
- حذف بخشهای اضافی
- تغییر فضای رنگ
بینایی ماشین
در Computer Vision هدف استخراج مفهوم یا اطلاعات از تصویر است.
برای مثال:
- چه اشیایی در تصویر وجود دارند؟
- محل هر شیء کجاست؟
- تصویر متعلق به کدام دسته است؟
- چه متنی در تصویر نوشته شده؟
- آیا میان دو تصویر تغییر مهمی رخ داده است؟
OpenCV برای پردازش کلاسیک تصویر بسیار قدرتمند است و میتواند پیشپردازش ورودی مدلهای هوش مصنوعی را نیز انجام دهد.
نصب OpenCV در پایتون
ابتدا محیط مجازی بسازید:
python -m venv .venv
فعالسازی در ویندوز:
.venv\Scripts\activate
فعالسازی در Linux یا macOS:
source .venv/bin/activate
بهروزرسانی Pip:
python -m pip install --upgrade pip
نصب نسخه اصلی:
python -m pip install opencv-python
برای مثالهای این آموزش:
python -m pip install opencv-python numpy matplotlib pillow requests
بررسی نصب:
python -c "import cv2; print(cv2.__version__)"
نکته مهم این است که نام پکیج هنگام نصب opencv-python و نام ماژول هنگام Import برابر cv2 است:
import cv2
نباید از دستور زیر استفاده کنید:
pip install cv2
تفاوت پکیجهای OpenCV
چند پکیج رایج برای OpenCV در PyPI وجود دارد.
| پکیج | کاربرد |
|---|---|
opencv-python | ماژولهای اصلی همراه قابلیتهای GUI |
opencv-contrib-python | ماژولهای اصلی بهعلاوه Contrib |
opencv-python-headless | نسخه بدون وابستگیهای GUI برای سرور |
opencv-contrib-python-headless | نسخه Contrib بدون GUI |
برای لپتاپ و اجرای cv2.imshow:
python -m pip install opencv-python
برای سرور، Docker، FastAPI یا محیطی که پنجره گرافیکی ندارد:
python -m pip install opencv-python-headless
برای قابلیتهای اضافه Contrib:
python -m pip install opencv-contrib-python
معمولاً نباید چند نسخه مختلف را همزمان در یک محیط نصب کنید. اگر قبلاً چند نسخه نصب شدهاند:
python -m pip uninstall \
opencv-python \
opencv-contrib-python \
opencv-python-headless \
opencv-contrib-python-headless
سپس فقط نسخه موردنیاز را نصب کنید.
تصویر در OpenCV چگونه نمایش داده میشود؟
تصویر در OpenCV یک آرایه NumPy است.
import cv2
image = cv2.imread(
"sample.jpg"
)
print(type(image))
print(image.shape)
print(image.dtype)
برای یک تصویر رنگی، Shape معمولاً چنین است:
(height, width, channels)
برای مثال:
(900, 1600, 3)
یعنی:
- ارتفاع: ۹۰۰ پیکسل
- عرض: ۱۶۰۰ پیکسل
- تعداد Channel: سه
مقادیر تصویر معمولی اغلب از نوع uint8 و در بازه صفر تا ۲۵۵ هستند.
ترتیب رنگ BGR در OpenCV
OpenCV تصاویر رنگی را بهصورت پیشفرض با ترتیب BGR میخواند:
Blue, Green, Red
اما ابزارهایی مانند Matplotlib معمولاً انتظار RGB دارند:
Red, Green, Blue
به همین دلیل اگر تصویر OpenCV را مستقیم با Matplotlib نمایش دهید، ممکن است رنگ قرمز و آبی جابهجا شوند.
تبدیل BGR به RGB:
rgb_image = cv2.cvtColor(
image,
cv2.COLOR_BGR2RGB
)
خواندن تصویر با cv2.imread
import cv2
image = cv2.imread(
"sample.jpg"
)
همیشه نتیجه را بررسی کنید:
if image is None:
raise FileNotFoundError(
"Image could not be loaded"
)
براساس راهنمای رسمی OpenCV، تابع imread تصویر را از مسیر مشخصشده میخواند و در قالب یک آرایه برمیگرداند.
حالتهای متداول:
color_image = cv2.imread(
"sample.jpg",
cv2.IMREAD_COLOR
)
gray_image = cv2.imread(
"sample.jpg",
cv2.IMREAD_GRAYSCALE
)
unchanged_image = cv2.imread(
"sample.png",
cv2.IMREAD_UNCHANGED
)
IMREAD_UNCHANGED میتواند Alpha Channel فایل PNG را نیز حفظ کند.
مشکل مسیر یا نام فارسی فایل
در بعضی محیطها، cv2.imread ممکن است با مسیرهای دارای نویسه فارسی یا Unicode مشکل داشته باشد. یک روش جایگزین:
import cv2
import numpy as np
def read_image_unicode(path):
file_bytes = np.fromfile(
path,
dtype=np.uint8
)
image = cv2.imdecode(
file_bytes,
cv2.IMREAD_UNCHANGED
)
if image is None:
raise ValueError(
f"Cannot decode image: {path}"
)
return image
استفاده:
image = read_image_unicode(
"تصاویر/محصول.jpg"
)
نمایش تصویر روی کامپیوتر
cv2.imshow(
"Image",
image
)
cv2.waitKey(0)
cv2.destroyAllWindows()
waitKey(0) منتظر فشردن یک کلید میماند.
در سرور یا نسخه Headless نمیتوانید از imshow استفاده کنید. در این محیطها تصویر را ذخیره کنید یا در Notebook با Matplotlib نمایش دهید.
نمایش تصویر در Jupyter و Google Colab
import matplotlib.pyplot as plt
rgb_image = cv2.cvtColor(
image,
cv2.COLOR_BGR2RGB
)
plt.figure(
figsize=(10, 6)
)
plt.imshow(rgb_image)
plt.axis("off")
plt.show()
برای تصویر Grayscale:
plt.imshow(
gray_image,
cmap="gray"
)
plt.axis("off")
plt.show()
ذخیره تصویر
success = cv2.imwrite(
"output.jpg",
image
)
if not success:
raise IOError(
"Image could not be saved"
)
تنظیم کیفیت JPEG:
cv2.imwrite(
"output.jpg",
image,
[
cv2.IMWRITE_JPEG_QUALITY,
90
]
)
تنظیم فشردهسازی PNG:
cv2.imwrite(
"output.png",
image,
[
cv2.IMWRITE_PNG_COMPRESSION,
6
]
)
کیفیت JPEG معمولاً بین صفر تا ۱۰۰ است. کیفیت بیشتر معمولاً حجم فایل را نیز افزایش میدهد.
ذخیره فایل با مسیر فارسی
from pathlib import Path
import cv2
def write_image_unicode(
path,
image,
extension=".jpg",
params=None
):
path = Path(path)
if params is None:
params = []
success, encoded = cv2.imencode(
extension,
image,
params
)
if not success:
raise ValueError(
"Image encoding failed"
)
encoded.tofile(
str(path)
)
استفاده:
write_image_unicode(
"خروجی/تصویر.jpg",
image,
extension=".jpg",
params=[
cv2.IMWRITE_JPEG_QUALITY,
90
]
)
دسترسی به پیکسلها
height, width = image.shape[:2]
print("Width:", width)
print("Height:", height)
دریافت پیکسل:
blue, green, red = image[
100,
200
]
print(blue, green, red)
تغییر یک پیکسل:
image[100, 200] = [
0,
0,
255
]
برای تغییر ناحیه، بهتر است از Slicing استفاده شود:
image[
50:150,
100:300
] = [0, 0, 255]
حلقهزدن روی تکتک پیکسلها در پایتون معمولاً کند است. عملیات برداری NumPy و توابع OpenCV انتخاب بهتری هستند.
Crop کردن تصویر
cropped = image[
100:500,
200:900
]
ترتیب Slicing چنین است:
image[y1:y2, x1:x2]
اعتبارسنجی مختصات:
x1 = max(
0,
min(x1, width)
)
x2 = max(
0,
min(x2, width)
)
y1 = max(
0,
min(y1, height)
)
y2 = max(
0,
min(y2, height)
)
if x2 <= x1 or y2 <= y1:
raise ValueError(
"Invalid crop coordinates"
)
cropped = image[
y1:y2,
x1:x2
]
تغییر اندازه تصویر
resized = cv2.resize(
image,
(800, 450)
)
در OpenCV ترتیب اندازه برابر است با:
(width, height)
نه ارتفاع و عرض.
حفظ نسبت تصویر
def resize_keep_aspect(
image,
target_width=None,
target_height=None
):
height, width = image.shape[:2]
if (
target_width is None
and target_height is None
):
return image.copy()
if target_width is not None:
scale = (
target_width / width
)
new_width = target_width
new_height = int(
height * scale
)
else:
scale = (
target_height / height
)
new_height = target_height
new_width = int(
width * scale
)
interpolation = (
cv2.INTER_AREA
if scale < 1
else cv2.INTER_CUBIC
)
return cv2.resize(
image,
(
new_width,
new_height
),
interpolation=interpolation
)
استفاده:
small_image = resize_keep_aspect(
image,
target_width=800
)
روشهای رایج Interpolation:
| روش | کاربرد معمول |
|---|---|
INTER_AREA | کوچککردن تصویر |
INTER_LINEAR | تغییر اندازه عمومی |
INTER_CUBIC | بزرگکردن با کیفیت بهتر و سرعت کمتر |
INTER_NEAREST | Mask و Labelهای دستهای |
برای Resize کردن Mask دستهبندی از INTER_NEAREST استفاده کنید تا مقدارهای Class با هم ترکیب نشوند.
Rotate کردن تصویر
چرخش ۹۰ درجه:
rotated = cv2.rotate(
image,
cv2.ROTATE_90_CLOCKWISE
)
گزینههای دیگر:
cv2.ROTATE_90_COUNTERCLOCKWISE
cv2.ROTATE_180
چرخش با زاویه دلخواه:
height, width = image.shape[:2]
center = (
width / 2,
height / 2
)
rotation_matrix = (
cv2.getRotationMatrix2D(
center,
angle=15,
scale=1.0
)
)
rotated = cv2.warpAffine(
image,
rotation_matrix,
(
width,
height
),
borderMode=cv2.BORDER_REPLICATE
)
این روش ممکن است گوشههای تصویر را Crop کند. اگر حفظ تمام تصویر مهم است باید اندازه Canvas جدید محاسبه شود.
Flip کردن تصویر
افقی:
horizontal = cv2.flip(
image,
1
)
عمودی:
vertical = cv2.flip(
image,
0
)
هر دو جهت:
both = cv2.flip(
image,
-1
)
Flip در Data Augmentation نیز کاربرد دارد، اما باید با معنای داده سازگار باشد.
تبدیل فضای رنگ
BGR به RGB
rgb = cv2.cvtColor(
image,
cv2.COLOR_BGR2RGB
)
BGR به Grayscale
gray = cv2.cvtColor(
image,
cv2.COLOR_BGR2GRAY
)
BGR به HSV
hsv = cv2.cvtColor(
image,
cv2.COLOR_BGR2HSV
)
BGR به LAB
lab = cv2.cvtColor(
image,
cv2.COLOR_BGR2LAB
)
کاربردهای متداول:
- Grayscale: Threshold، Edge Detection و کاهش حجم محاسبات
- HSV: جداسازی رنگ از روشنایی
- LAB: تحلیل روشنایی و تفاوت رنگ
- RGB: نمایش در Matplotlib و بسیاری از مدلها
جداسازی یک محدوده رنگ با HSV
import numpy as np
hsv = cv2.cvtColor(
image,
cv2.COLOR_BGR2HSV
)
lower_blue = np.array([
90,
70,
50
])
upper_blue = np.array([
130,
255,
255
])
mask = cv2.inRange(
hsv,
lower_blue,
upper_blue
)
result = cv2.bitwise_and(
image,
image,
mask=mask
)
محدوده رنگ باید با تصویر، نور محیط و دوربین تنظیم شود. Threshold ثابت در شرایط نوری متفاوت همیشه نتیجه یکسانی تولید نمیکند.
کاهش Noise و Blur
Gaussian Blur
blurred = cv2.GaussianBlur(
image,
(5, 5),
0
)
اندازه Kernel باید معمولاً فرد باشد.
Median Blur
median = cv2.medianBlur(
image,
5
)
Median Blur برای بعضی Noiseهای نقطهای مناسب است.
Bilateral Filter
bilateral = cv2.bilateralFilter(
image,
d=9,
sigmaColor=75,
sigmaSpace=75
)
Bilateral Filter تلاش میکند همزمان Noise را کاهش و لبهها را بهتر حفظ کند، اما از Gaussian Blur کندتر است.
Blur بیش از حد میتواند جزئیات واقعی تصویر را از بین ببرد.
افزایش Contrast با Histogram Equalization
برای تصویر Grayscale:
gray = cv2.cvtColor(
image,
cv2.COLOR_BGR2GRAY
)
equalized = cv2.equalizeHist(
gray
)
برای کنترل محلی بهتر میتوان از CLAHE استفاده کرد:
clahe = cv2.createCLAHE(
clipLimit=2.0,
tileGridSize=(8, 8)
)
enhanced = clahe.apply(
gray
)
CLAHE تصویر را به ناحیههای کوچک تقسیم میکند و Contrast را بهصورت محلی تنظیم میکند.
افزایش Contrast همیشه به معنای افزایش اطلاعات واقعی تصویر نیست و ممکن است Noise را نیز برجسته کند.
Threshold کردن تصویر
Threshold یک تصویر Grayscale را به Mask دودویی تبدیل میکند.
Threshold ثابت
gray = cv2.cvtColor(
image,
cv2.COLOR_BGR2GRAY
)
threshold_value, binary = cv2.threshold(
gray,
127,
255,
cv2.THRESH_BINARY
)
Otsu Threshold
threshold_value, binary = cv2.threshold(
gray,
0,
255,
cv2.THRESH_BINARY
+ cv2.THRESH_OTSU
)
print(
"Selected threshold:",
threshold_value
)
Otsu بهصورت خودکار یک Threshold سراسری انتخاب میکند و برای بعضی تصاویر دارای Foreground و Background مشخص مناسب است.
Adaptive Threshold
adaptive = cv2.adaptiveThreshold(
gray,
255,
cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY,
blockSize=21,
C=7
)
Adaptive Threshold برای تصاویری با نور نامتوازن، مانند عکس سند، مفید است.
blockSize باید عددی فرد و بزرگتر از یک باشد.
عملیات Morphology
Morphology روی Maskهای دودویی انجام میشود.
ساخت Kernel:
kernel = cv2.getStructuringElement(
cv2.MORPH_RECT,
(5, 5)
)
Erosion
eroded = cv2.erode(
binary,
kernel,
iterations=1
)
Erosion ناحیه سفید را کوچکتر میکند.
Dilation
dilated = cv2.dilate(
binary,
kernel,
iterations=1
)
Dilation ناحیه سفید را بزرگتر میکند.
Opening
opened = cv2.morphologyEx(
binary,
cv2.MORPH_OPEN,
kernel
)
Opening میتواند Noiseهای سفید کوچک را حذف کند.
Closing
closed = cv2.morphologyEx(
binary,
cv2.MORPH_CLOSE,
kernel
)
Closing میتواند فاصلهها و سوراخهای کوچک را پر کند.
معنای عملیات به این بستگی دارد که Foreground در Mask سفید یا سیاه باشد.
تشخیص لبه با Canny
gray = cv2.cvtColor(
image,
cv2.COLOR_BGR2GRAY
)
blurred = cv2.GaussianBlur(
gray,
(5, 5),
0
)
edges = cv2.Canny(
blurred,
threshold1=50,
threshold2=150
)
مراحل معمول:
- تبدیل به Grayscale
- کاهش Noise
- اجرای Canny
- انجام Morphology در صورت نیاز
- پیداکردن Contour
Thresholdهای Canny باید براساس Contrast و Noise تصویر تنظیم شوند.
پیداکردن Contour
Contour مرز یک ناحیه متصل در تصویر دودویی است.
contours, hierarchy = (
cv2.findContours(
edges,
cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE
)
)
print(
"Contours:",
len(contours)
)
رسم تمام Contourها:
output = image.copy()
cv2.drawContours(
output,
contours,
contourIdx=-1,
color=(0, 255, 0),
thickness=2
)
فیلترکردن Contour براساس مساحت
minimum_area = 1000
large_contours = [
contour
for contour in contours
if cv2.contourArea(
contour
) >= minimum_area
]
مرتبسازی:
large_contours = sorted(
large_contours,
key=cv2.contourArea,
reverse=True
)
ساخت Bounding Box
output = image.copy()
for contour in large_contours:
x, y, width, height = (
cv2.boundingRect(
contour
)
)
cv2.rectangle(
output,
(x, y),
(
x + width,
y + height
),
(0, 255, 0),
2
)
Bounding Box چرخیده:
rectangle = cv2.minAreaRect(
large_contours[0]
)
box = cv2.boxPoints(
rectangle
)
box = np.int32(box)
cv2.drawContours(
output,
[box],
0,
(255, 0, 0),
2
)
محاسبه مرکز Contour
moments = cv2.moments(
contour
)
if moments["m00"] != 0:
center_x = int(
moments["m10"]
/ moments["m00"]
)
center_y = int(
moments["m01"]
/ moments["m00"]
)
رسم مرکز:
cv2.circle(
output,
(
center_x,
center_y
),
radius=5,
color=(0, 0, 255),
thickness=-1
)
رسم متن و شکل
Rectangle:
cv2.rectangle(
image,
(50, 50),
(350, 250),
(0, 255, 0),
3
)
Circle:
cv2.circle(
image,
(200, 200),
70,
(255, 0, 0),
3
)
Line:
cv2.line(
image,
(20, 20),
(500, 300),
(0, 0, 255),
2
)
متن انگلیسی:
cv2.putText(
image,
"Product",
(50, 40),
cv2.FONT_HERSHEY_SIMPLEX,
1,
(255, 255, 255),
2,
cv2.LINE_AA
)
نوشتن متن فارسی روی تصویر
cv2.putText پشتیبانی مناسبی از فونت و اتصال حروف فارسی ندارد. برای متن فارسی میتوان از Pillow استفاده کرد.
python -m pip install pillow
کد:
import cv2
import numpy as np
from PIL import (
Image,
ImageDraw,
ImageFont
)
def put_persian_text(
image,
text,
position,
font_path,
font_size=36,
color=(255, 255, 255)
):
rgb = cv2.cvtColor(
image,
cv2.COLOR_BGR2RGB
)
pil_image = Image.fromarray(
rgb
)
draw = ImageDraw.Draw(
pil_image
)
font = ImageFont.truetype(
font_path,
font_size
)
draw.text(
position,
text,
font=font,
fill=color,
direction="rtl"
)
result = cv2.cvtColor(
np.array(pil_image),
cv2.COLOR_RGB2BGR
)
return result
استفاده:
image = put_persian_text(
image=image,
text="گزارش پردازش تصویر",
position=(500, 40),
font_path="Vazirmatn-Regular.ttf",
font_size=42
)
پشتیبانی direction="rtl" به امکانات نصبشده Pillow و موتورهای متن محیط وابسته است. خروجی را در محیط نهایی بررسی کنید.
پردازش ویدئو با OpenCV
ویدئو مجموعهای از Frameهاست. OpenCV هر Frame را بهصورت یک تصویر NumPy در اختیار برنامه قرار میدهد.
خواندن فایل ویدئویی
import cv2
capture = cv2.VideoCapture(
"input.mp4"
)
if not capture.isOpened():
raise RuntimeError(
"Video could not be opened"
)
دریافت مشخصات:
fps = capture.get(
cv2.CAP_PROP_FPS
)
frame_count = int(
capture.get(
cv2.CAP_PROP_FRAME_COUNT
)
)
width = int(
capture.get(
cv2.CAP_PROP_FRAME_WIDTH
)
)
height = int(
capture.get(
cv2.CAP_PROP_FRAME_HEIGHT
)
)
duration = (
frame_count / fps
if fps > 0
else None
)
print("FPS:", fps)
print("Frames:", frame_count)
print("Size:", width, height)
print("Duration:", duration)
خواندن تمام Frameها
try:
while True:
success, frame = (
capture.read()
)
if not success:
break
gray = cv2.cvtColor(
frame,
cv2.COLOR_BGR2GRAY
)
cv2.imshow(
"Video",
gray
)
if (
cv2.waitKey(1)
& 0xFF
) == ord("q"):
break
finally:
capture.release()
cv2.destroyAllWindows()
استفاده از finally کمک میکند منبع ویدئو حتی در صورت خطا آزاد شود.
دریافت تصویر از وبکم
معمولاً دوربین پیشفرض Index صفر دارد:
capture = cv2.VideoCapture(
0
)
کد:
if not capture.isOpened():
raise RuntimeError(
"Camera could not be opened"
)
try:
while True:
success, frame = (
capture.read()
)
if not success:
break
cv2.imshow(
"Camera",
frame
)
if (
cv2.waitKey(1)
& 0xFF
) == ord("q"):
break
finally:
capture.release()
cv2.destroyAllWindows()
فقط در برنامهای که کاربر آگاهانه اجرا کرده است از دوربین استفاده کنید و دسترسی دوربین را شفاف نگه دارید.
ذخیره ویدئوی پردازششده
capture = cv2.VideoCapture(
"input.mp4"
)
fps = capture.get(
cv2.CAP_PROP_FPS
)
width = int(
capture.get(
cv2.CAP_PROP_FRAME_WIDTH
)
)
height = int(
capture.get(
cv2.CAP_PROP_FRAME_HEIGHT
)
)
fourcc = cv2.VideoWriter_fourcc(
*"mp4v"
)
writer = cv2.VideoWriter(
"output.mp4",
fourcc,
fps,
(
width,
height
)
)
if not writer.isOpened():
capture.release()
raise RuntimeError(
"Output video could not be opened"
)
try:
while True:
success, frame = (
capture.read()
)
if not success:
break
processed = cv2.GaussianBlur(
frame,
(5, 5),
0
)
writer.write(
processed
)
finally:
capture.release()
writer.release()
اندازه Frame ورودی writer.write باید با اندازه تعریفشده در VideoWriter یکسان باشد.
استخراج Frame از ویدئو
برای ذخیره یک Frame در هر ثانیه:
from pathlib import Path
import cv2
output_directory = Path(
"frames"
)
output_directory.mkdir(
parents=True,
exist_ok=True
)
capture = cv2.VideoCapture(
"input.mp4"
)
fps = capture.get(
cv2.CAP_PROP_FPS
)
if fps <= 0:
capture.release()
raise ValueError(
"Invalid video FPS"
)
frame_interval = max(
1,
round(fps)
)
frame_index = 0
saved_index = 0
try:
while True:
success, frame = (
capture.read()
)
if not success:
break
if (
frame_index
% frame_interval
== 0
):
output_path = (
output_directory
/ f"frame-{saved_index:05d}.jpg"
)
cv2.imwrite(
str(output_path),
frame
)
saved_index += 1
frame_index += 1
finally:
capture.release()
برای تحلیل ویدئو با مدل چندوجهی، معمولاً لازم نیست تمام Frameها ارسال شوند. ابتدا Frameهای نماینده را انتخاب کنید.
پروژه عملی: ساخت اسکنر ساده سند
هدف پروژه:
- خواندن عکس سند
- تغییر اندازه برای پردازش سریعتر
- تبدیل به Grayscale
- کاهش Noise
- تشخیص لبه
- پیداکردن بزرگترین Contour چهارضلعی
- اصلاح Perspective
- ساخت خروجی شبیه اسکن
تابع مرتبکردن چهار نقطه
چهار گوشه باید به ترتیب زیر مرتب شوند:
- بالا چپ
- بالا راست
- پایین راست
- پایین چپ
import numpy as np
def order_points(points):
rectangle = np.zeros(
(4, 2),
dtype=np.float32
)
sums = points.sum(
axis=1
)
differences = np.diff(
points,
axis=1
).reshape(-1)
rectangle[0] = points[
np.argmin(sums)
]
rectangle[2] = points[
np.argmax(sums)
]
rectangle[1] = points[
np.argmin(differences)
]
rectangle[3] = points[
np.argmax(differences)
]
return rectangle
تابع اصلاح Perspective
import cv2
import numpy as np
def four_point_transform(
image,
points
):
rectangle = order_points(
points
)
top_left, top_right, \
bottom_right, bottom_left = (
rectangle
)
width_a = np.linalg.norm(
bottom_right
- bottom_left
)
width_b = np.linalg.norm(
top_right
- top_left
)
max_width = int(
max(
width_a,
width_b
)
)
height_a = np.linalg.norm(
top_right
- bottom_right
)
height_b = np.linalg.norm(
top_left
- bottom_left
)
max_height = int(
max(
height_a,
height_b
)
)
destination = np.array(
[
[0, 0],
[
max_width - 1,
0
],
[
max_width - 1,
max_height - 1
],
[
0,
max_height - 1
]
],
dtype=np.float32
)
matrix = (
cv2.getPerspectiveTransform(
rectangle,
destination
)
)
warped = cv2.warpPerspective(
image,
matrix,
(
max_width,
max_height
)
)
return warped
پیداکردن مرز سند
import cv2
import numpy as np
image = cv2.imread(
"document.jpg"
)
if image is None:
raise FileNotFoundError(
"document.jpg was not found"
)
original = image.copy()
original_height = (
image.shape[0]
)
target_height = 700
scale = (
original_height
/ target_height
)
resized_width = int(
image.shape[1]
/ scale
)
resized = cv2.resize(
image,
(
resized_width,
target_height
),
interpolation=cv2.INTER_AREA
)
gray = cv2.cvtColor(
resized,
cv2.COLOR_BGR2GRAY
)
blurred = cv2.GaussianBlur(
gray,
(5, 5),
0
)
edges = cv2.Canny(
blurred,
50,
150
)
kernel = cv2.getStructuringElement(
cv2.MORPH_RECT,
(5, 5)
)
edges = cv2.morphologyEx(
edges,
cv2.MORPH_CLOSE,
kernel
)
پیداکردن Contourها:
contours, _ = cv2.findContours(
edges,
cv2.RETR_LIST,
cv2.CHAIN_APPROX_SIMPLE
)
contours = sorted(
contours,
key=cv2.contourArea,
reverse=True
)[:10]
پیداکردن چهارضلعی:
document_contour = None
for contour in contours:
perimeter = cv2.arcLength(
contour,
True
)
approximation = (
cv2.approxPolyDP(
contour,
0.02 * perimeter,
True
)
)
if len(approximation) == 4:
document_contour = (
approximation.reshape(
4,
2
)
)
break
if document_contour is None:
raise RuntimeError(
"Document boundary was not detected"
)
اصلاح Perspective روی تصویر اصلی:
original_points = (
document_contour
* scale
)
scanned = four_point_transform(
original,
original_points
)
ساخت نسخه سیاهوسفید:
scanned_gray = cv2.cvtColor(
scanned,
cv2.COLOR_BGR2GRAY
)
scanned_output = (
cv2.adaptiveThreshold(
scanned_gray,
255,
cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY,
31,
10
)
)
cv2.imwrite(
"scanned-document.png",
scanned_output
)
این پروژه برای سندهایی با کنتراست مناسب و پسزمینه نسبتاً ساده عملکرد بهتری دارد. در عکسهای پیچیده باید پارامترها و روش تشخیص مرز بهبود پیدا کنند.
ساخت Pipeline پردازش دستهای تصویر
فرض کنید میخواهید تمام تصاویر یک پوشه را:
- بخوانید
- جهت EXIF را اصلاح کنید
- به عرض مشخص Resize کنید
- در قالب WebP ذخیره کنید
OpenCV بهتنهایی همیشه Orientation ثبتشده در EXIF را مانند همه ابزارها مدیریت نمیکند. برای خواندن مطمئن Orientation میتوان از Pillow استفاده کرد.
from pathlib import Path
from PIL import (
Image,
ImageOps
)
import cv2
import numpy as np
def load_with_orientation(path):
with Image.open(path) as pil_image:
pil_image = (
ImageOps.exif_transpose(
pil_image
)
)
pil_image = (
pil_image.convert("RGB")
)
rgb = np.array(
pil_image
)
return cv2.cvtColor(
rgb,
cv2.COLOR_RGB2BGR
)
پردازش پوشه:
input_directory = Path(
"input-images"
)
output_directory = Path(
"output-images"
)
output_directory.mkdir(
parents=True,
exist_ok=True
)
allowed_extensions = {
".jpg",
".jpeg",
".png",
".webp"
}
for input_path in (
input_directory.iterdir()
):
if (
input_path.suffix.lower()
not in allowed_extensions
):
continue
try:
image = load_with_orientation(
input_path
)
processed = resize_keep_aspect(
image,
target_width=1200
)
output_path = (
output_directory
/ (
input_path.stem
+ ".webp"
)
)
success = cv2.imwrite(
str(output_path),
processed,
[
cv2.IMWRITE_WEBP_QUALITY,
85
]
)
if not success:
raise IOError(
"Save failed"
)
print(
"Saved:",
output_path
)
except Exception as error:
print(
"Failed:",
input_path,
error
)
آمادهسازی تصویر برای مدل هوش مصنوعی
پیش از ارسال تصویر به مدل میتوانید:
- جهت تصویر را اصلاح کنید
- ابعاد بسیار بزرگ را کاهش دهید
- فرمت را به JPEG یا WebP تبدیل کنید
- اطلاعات غیرضروری را Crop کنید
- کیفیت را در حد مناسب نگه دارید
- حجم Base64 را کنترل کنید
تابع آمادهسازی:
import cv2
def prepare_image_for_api(
image,
max_width=1600,
jpeg_quality=88
):
height, width = image.shape[:2]
if width > max_width:
scale = (
max_width / width
)
image = cv2.resize(
image,
(
max_width,
int(height * scale)
),
interpolation=cv2.INTER_AREA
)
success, encoded = cv2.imencode(
".jpg",
image,
[
cv2.IMWRITE_JPEG_QUALITY,
jpeg_quality
]
)
if not success:
raise ValueError(
"JPEG encoding failed"
)
return encoded.tobytes()
اتصال OpenCV به API چندوجهی درواره
OpenCV برای پردازش قطعی تصویر مناسب است. مدل چندوجهی میتواند محتوای تصویر را توصیف یا اطلاعات سطح بالاتر آن را تحلیل کند.
فرایند پیشنهادی:
- تصویر با OpenCV خوانده شود
- Resize و Crop انجام شود
- تصویر Encode شود
- به یک مدل دارای قابلیت ورودی تصویر ارسال شود
- خروجی مدل اعتبارسنجی شود
برای دسترسی به مدلهای دارای قابلیت تصویر میتوانید در درواره ثبتنام کنید. مدل مناسب و قیمت بهروز آن را در صفحه مدلهای درواره بررسی کنید.
نگهداری API Key
در Linux یا macOS:
export DARVAREH_API_KEY="YOUR_DARVAREH_API_KEY"
در PowerShell:
$env:DARVAREH_API_KEY="YOUR_DARVAREH_API_KEY"
تبدیل تصویر به Data URL
import base64
image_bytes = (
prepare_image_for_api(
image,
max_width=1600,
jpeg_quality=88
)
)
image_base64 = base64.b64encode(
image_bytes
).decode("utf-8")
data_url = (
"data:image/jpeg;base64,"
+ image_base64
)
ارسال تصویر به API درواره
import os
import requests
response = requests.post(
"https://api.darvareh.ir/v1/chat/completions",
headers={
"Authorization": (
f"Bearer "
f"{os.environ['DARVAREH_API_KEY']}"
),
"Content-Type": "application/json"
},
json={
"model": "YOUR_MODEL_ID",
"messages": [
{
"role": "system",
"content": (
"تو یک دستیار تحلیل تصویر هستی. "
"فقط مواردی را بیان کن که از تصویر "
"قابل مشاهدهاند. اگر مطمئن نیستی، "
"عدم قطعیت را شفاف اعلام کن."
)
},
{
"role": "user",
"content": [
{
"type": "text",
"text": (
"این تصویر را به فارسی "
"توصیف کن و عناصر اصلی، "
"متن قابل مشاهده و کیفیت "
"کلی تصویر را در قالب "
"بخشهای جداگانه بنویس."
)
},
{
"type": "image_url",
"image_url": {
"url": data_url
}
}
]
}
],
"temperature": 0.1
},
timeout=90
)
response.raise_for_status()
result = response.json()
print(
result["choices"][0]
["message"]
["content"]
)
YOUR_MODEL_ID را با شناسه یکی از مدلهای دارای قابلیت ورودی تصویر در درواره جایگزین کنید.
ترکیب OpenCV و مدل چندوجهی
تقسیم وظایف مناسب:
| وظیفه | ابزار پیشنهادی |
|---|---|
| Resize و Crop | OpenCV |
| تبدیل رنگ | OpenCV |
| Threshold و Edge | OpenCV |
| Contour و Bounding Box | OpenCV |
| فشردهسازی و Encode | OpenCV |
| توصیف معنایی تصویر | مدل چندوجهی |
| استخراج پاسخ زبانی | مدل چندوجهی |
| ساخت گزارش فارسی | مدل زبانی یا چندوجهی |
| دسترسی API به مدل | درواره |
اگر کاری را میتوان با یک الگوریتم قطعی، سریع و کمهزینه انجام داد، لازم نیست آن را به مدل زبانی واگذار کنید.
پردازش چند تصویر با کنترل خطا
from pathlib import Path
import cv2
def process_image_file(
input_path,
output_path
):
image = cv2.imread(
str(input_path)
)
if image is None:
raise ValueError(
"Image could not be read"
)
gray = cv2.cvtColor(
image,
cv2.COLOR_BGR2GRAY
)
enhanced = cv2.createCLAHE(
clipLimit=2.0,
tileGridSize=(8, 8)
).apply(gray)
success = cv2.imwrite(
str(output_path),
enhanced
)
if not success:
raise IOError(
"Output could not be saved"
)
اجرای دستهای:
input_directory = Path(
"input"
)
output_directory = Path(
"output"
)
output_directory.mkdir(
parents=True,
exist_ok=True
)
for input_path in (
input_directory.glob("*.jpg")
):
output_path = (
output_directory
/ input_path.name
)
try:
process_image_file(
input_path,
output_path
)
print(
"Processed:",
input_path.name
)
except Exception as error:
print(
"Failed:",
input_path.name,
error
)
بهینهسازی OpenCV
کاهش اندازه پیش از پردازش
اگر برای تشخیص اولیه به رزولوشن کامل نیاز ندارید، ابتدا تصویر را کوچک کنید.
استفاده از ROI
فقط Region of Interest را پردازش کنید:
roi = image[
y1:y2,
x1:x2
]
پرهیز از Copy غیرضروری
working_image = image.copy()
فقط وقتی Copy نیاز دارید که نمیخواهید تصویر اصلی تغییر کند.
استفاده از عملیات برداری
بهجای Loop روی پیکسلها از NumPy و OpenCV استفاده کنید.
پردازش انتخابی Frameهای ویدئو
برای تحلیل غیر Real-time لازم نیست تمام Frameها پردازش شوند:
if frame_index % 5 != 0:
frame_index += 1
continue
استفاده از نسخه Headless روی سرور
python -m pip install opencv-python-headless
آزادکردن منابع
capture.release()
writer.release()
cv2.destroyAllWindows()
ساخت API پردازش تصویر با FastAPI
نصب:
python -m pip install \
fastapi \
uvicorn \
python-multipart \
opencv-python-headless \
numpy
فایل main.py:
import cv2
import numpy as np
from fastapi import (
FastAPI,
File,
HTTPException,
UploadFile
)
from fastapi.responses import Response
app = FastAPI()
MAX_FILE_SIZE = (
10 * 1024 * 1024
)
@app.post("/grayscale")
async def grayscale_image(
file: UploadFile = File(...)
):
if file.content_type not in {
"image/jpeg",
"image/png",
"image/webp"
}:
raise HTTPException(
status_code=415,
detail="Unsupported image type"
)
contents = await file.read()
if len(contents) > MAX_FILE_SIZE:
raise HTTPException(
status_code=413,
detail="Image is too large"
)
array = np.frombuffer(
contents,
dtype=np.uint8
)
image = cv2.imdecode(
array,
cv2.IMREAD_COLOR
)
if image is None:
raise HTTPException(
status_code=400,
detail="Invalid image"
)
gray = cv2.cvtColor(
image,
cv2.COLOR_BGR2GRAY
)
success, encoded = cv2.imencode(
".png",
gray
)
if not success:
raise HTTPException(
status_code=500,
detail="Encoding failed"
)
return Response(
content=encoded.tobytes(),
media_type="image/png"
)
اجرا:
uvicorn main:app --reload
آزمایش با cURL:
curl \
-X POST \
-F "file=@sample.jpg" \
http://127.0.0.1:8000/grayscale \
--output grayscale.png
اعتبارسنجی Content Type بهتنهایی کافی نیست؛ به همین دلیل کد تلاش میکند محتوای واقعی فایل را نیز Decode کند.
خطاهای رایج OpenCV
خطای نصب نبودن cv2
ModuleNotFoundError: No module named 'cv2'
راهحل:
python -m pip install opencv-python
یا روی سرور:
python -m pip install opencv-python-headless
خروجی imread برابر None است
image = cv2.imread(
path
)
if image is None:
print(
"Invalid path or file"
)
موارد زیر را بررسی کنید:
- مسیر فایل
- Working Directory
- سطح دسترسی
- فرمت تصویر
- خرابی فایل
- نویسههای Unicode در مسیر
نمایش مسیر فعلی:
from pathlib import Path
print(Path.cwd())
رنگ تصویر اشتباه است
برای Matplotlib:
rgb = cv2.cvtColor(
image,
cv2.COLOR_BGR2RGB
)
خطای imshow روی سرور
نسخه Headless پنجره GUI ندارد. از imwrite یا نمایش تحت وب استفاده کنید.
خطای اندازه در VideoWriter
تمام Frameهای ورودی باید دقیقاً با Width و Height تعریفشده هنگام ساخت Writer یکسان باشند.
خروجی ویدئو اجرا نمیشود
Codec و Container باید با محیط سازگار باشند. گاهی لازم است Codec یا پسوند خروجی تغییر کند.
متن فارسی خراب نمایش داده میشود
از Pillow و فونت فارسی استفاده کنید. cv2.putText برای متن فارسی انتخاب مناسبی نیست.
Canny هیچ لبهای پیدا نمیکند
موارد زیر را بررسی کنید:
- تصویر بیش از حد تار است
- Thresholdها زیاد هستند
- Contrast پایین است
- تصویر پیش از Canny به Grayscale تبدیل نشده
- Noise زیاد است
Contourهای خیلی زیاد پیدا میشوند
راهکارها:
- Blur پیش از Canny
- Morphology
- افزایش حداقل مساحت
- استفاده از
RETR_EXTERNAL - محدودکردن ROI
مصرف حافظه زیاد
تصویر 4K و ویدئو میتوانند حافظه زیادی مصرف کنند. Frameها را نگهداری نکنید مگر اینکه واقعاً لازم باشند.
اشتباهات رایج در پردازش تصویر
پردازش تصویر بدون بررسی BGR و RGB
این اشتباه میتواند خروجی مدل، نمودار یا ذخیرهسازی را تغییر دهد.
Resize کردن بدون حفظ نسبت
تصویر کشیده یا فشرده میشود و شکل اشیا تغییر میکند.
استفاده از Threshold ثابت برای همه تصاویر
نور، دوربین، پسزمینه و Exposure روی مقدار مناسب Threshold اثر دارند.
اعمال Blur شدید
Blur زیاد میتواند لبه و متن واقعی را حذف کند.
حذف Contour کوچک بدون شناخت مسئله
ممکن است شیء موردنظر واقعاً کوچک باشد.
اعتماد کامل به مدل چندوجهی
مدل ممکن است بخشی از تصویر را اشتباه تفسیر کند. خروجی مهم باید با داده یا بررسی انسانی اعتبارسنجی شود.
ارسال تصویر بسیار بزرگ به API
رزولوشن بیش از نیاز میتواند حجم، Latency و هزینه را افزایش دهد.
ارسال اطلاعات تصویری غیرضروری
پیش از ارسال، ناحیههای غیرمرتبط را Crop کنید و فقط داده لازم را پردازش کنید. تصاویر شخصی یا محرمانه را تنها با مجوز و ضرورت مشخص استفاده کنید.
چکلیست پروژه OpenCV
- نسخه مناسب OpenCV نصب شده است
- چند پکیج OpenCV همزمان نصب نشدهاند
- نتیجه
imreadبررسی میشود - تفاوت BGR و RGB رعایت شده است
- نسبت تصویر هنگام Resize حفظ میشود
- Interpolation مناسب انتخاب شده است
- مسیرهای Unicode آزمایش شدهاند
- نوع و Shape آرایه کنترل میشود
- Threshold براساس داده تنظیم شده است
- Contourها براساس منطق مسئله فیلتر میشوند
- منابع VideoCapture و VideoWriter آزاد میشوند
- حجم فایل ورودی API محدود شده است
- فایل واقعی Decode و اعتبارسنجی میشود
- تصویر پیش از ارسال به مدل Resize شده است
- اطلاعات غیرضروری Crop شدهاند
- خروجی مدل چندوجهی اعتبارسنجی میشود
- API Key داخل کد ذخیره نشده است
پرسشهای متداول
OpenCV چیست؟
OpenCV یک کتابخانه متنباز برای پردازش تصویر، ویدئو و بینایی ماشین است که از زبانهایی مانند C++، پایتون و Java پشتیبانی میکند.
چگونه OpenCV را در پایتون نصب کنیم؟
python -m pip install opencv-python
سپس:
import cv2
چرا نام پکیج OpenCV و نام Import آن cv2 است؟
پکیج پایتون با نام opencv-python منتشر میشود، اما نام تاریخی ماژول پایتون آن cv2 است.
تفاوت opencv-python و opencv-python-headless چیست؟
نسخه Headless وابستگیهای رابط گرافیکی را ندارد و برای سرور، Docker و API مناسبتر است. در آن نباید به cv2.imshow وابسته باشید.
چرا رنگ تصویر در Matplotlib اشتباه نمایش داده میشود؟
OpenCV تصویر را بهشکل BGR میخواند، اما Matplotlib انتظار RGB دارد:
rgb = cv2.cvtColor(
image,
cv2.COLOR_BGR2RGB
)
چگونه تصویر را Grayscale کنیم؟
gray = cv2.cvtColor(
image,
cv2.COLOR_BGR2GRAY
)
چگونه لبه تصویر را پیدا کنیم؟
edges = cv2.Canny(
gray,
50,
150
)
معمولاً بهتر است پیش از Canny، Noise تصویر کاهش پیدا کند.
Contour چیست؟
Contour مرز یک ناحیه متصل در تصویر دودویی است و برای پیداکردن شکل، Bounding Box و مساحت اشیا استفاده میشود.
آیا OpenCV مدل هوش مصنوعی است؟
خیر. OpenCV یک کتابخانه پردازش تصویر و بینایی ماشین است. بعضی ماژولهای آن امکان اجرای مدلهای یادگیری ماشین و شبکه عصبی را نیز فراهم میکنند.
OpenCV بهتر است یا مدل چندوجهی؟
کاربرد آنها متفاوت است. OpenCV برای عملیات قطعی مانند Resize، Threshold، Edge و Contour مناسب است. مدل چندوجهی برای درک معنایی و توصیف محتوای تصویر کاربرد دارد.
چگونه تصویر OpenCV را به API درواره ارسال کنیم؟
تصویر را با cv2.imencode به JPEG یا PNG تبدیل کنید، آن را با Base64 داخل Data URL قرار دهید و به یک مدل دارای قابلیت ورودی تصویر ارسال کنید.
جمعبندی
OpenCV یکی از مهمترین ابزارهای برنامهنویسی برای پردازش تصویر و ویدئو است. با یادگیری ساختار تصویر، فضای رنگ، Filter، Threshold، Morphology، Edge و Contour میتوانید بسیاری از Pipelineهای کاربردی Computer Vision را پیادهسازی کنید.
یک معماری مناسب میتواند چنین تقسیم وظیفهای داشته باشد:
- NumPy برای آرایه و محاسبات پیکسلی
- OpenCV برای پردازش تصویر و ویدئو
- Pillow برای فونت فارسی و Metadata تصویر
- مدل چندوجهی برای درک معنایی تصویر
- FastAPI برای ارائه سرویس پردازش
- درواره برای دسترسی API به مدلهای هوش مصنوعی
برای شروع، در درواره ثبتنام کنید، API Key بگیرید و یک مدل دارای قابلیت پردازش تصویر را از صفحه مدلها و قیمتها انتخاب کنید.
مقالات مرتبط
- آموزش API چندوجهی و ارسال تصویر به مدل هوش مصنوعی
- تبدیل عکس به متن با هوش مصنوعی و OCR
- آموزش افزایش کیفیت عکس با هوش مصنوعی
- آموزش حذف پسزمینه عکس با هوش مصنوعی
- راهنمای ویرایش عکس با هوش مصنوعی
- آموزش دریافت API Key هوش مصنوعی
برای مطالعه شرایط استفاده و محدودیتهای مسئولیت، صفحه «سلب مسئولیت» را مشاهده کنید.