суббота, 19 февраля 2011 г.

Работа с матрицами и изображениями

Эта тема вызвала довольно много проблем из-за различий в языках, но в итоге все довольно просто.
Основные проблемы:
1 - создание массива cvMat из имеющегося тьюпла
2 - чтение данных из массива
3 - сама технология создания нового массива

Итак, посмотрим, что у меня получилось:

Программа №1 - создание матрицы, работа с ее элементами
import cv,numpy

def main():
 mat=cv.CreateMat(2,2,cv.CV_32FC1)
 mat[1,1]=2.0
 print mat[1,1]
 print cv.Get2D(mat,1,1)
 return '****'
 
def sub(): 
 vals=((1.0,2.0),(3.0,4.0))
 a=numpy.array(vals)
 b=cv.fromarray(a)
 mat=cv.CreateMatHeader(2,2,cv.CV_32FC1)
 cv.SetData(mat,b.tostring())
 print mat[1,1]
 print cv.Get2D(mat,1,1)
 return '****'

if __name__ == '__main__':
 print main()
 print sub()
Коментарии - т.к. в питоне, как я уже говорил, нельзя работать с указателями и памятью, то все структуры были сильно упрощены, итого мы имеем: данные в cvMat хранятся в виде строки, доступ можно осуществлять, как к обычному массиву (mat[1,1]) ну и самое трудное - засунуть имеющийся тьюпл в cvMat, для этого я использовал библиотеку numpy(которая широко применяется параллельно с OpenCV для решения задач), а схема проста, мы имеем тьюпл, с помощью array() создаем для него интерфейс массива, затем с помощью formarray делаем массив типа cvMat ну и наконец присваиваем ему его новый заголовок (имя) (конечно можно остановиться после formarray, но хотелось попробовать использовать SetData). Так же, как мы видим cv.Get*D возвращает значение типа cvScalar, а не float, которое мы записывали.

Программа №2 - ROI несколько способов задания области интересов
import cv

def roi():
 cv.SetImageROI(src,rctBlue)
 cv.AddS(src,offsetBlue,src)
 cv.ResetImageROI(src)
 cv.SetImageROI(src,rctGreen)
 cv.AddS(src,offsetGreen,src)
 cv.ResetImageROI(src)
 cv.SetImageROI(src,rctRed)
 cv.AddS(src,offsetRed,src)
 cv.ResetImageROI(src)
 cv.ShowImage("Main",src)
 return 0

def subimage(): 
 subimg=cv.GetSubRect(src2, rctBlue)
 subimg2=cv.GetSubRect(src2, rctGreen)
 subimg3=cv.GetSubRect(src2, rctRed)
 cv.AddS(subimg,offsetBlue,subimg)
 cv.AddS(subimg2,offsetGreen,subimg2)
 cv.AddS(subimg3,offsetRed,subimg3)
 cv.ShowImage('Sub',src2)
 return 0
 
def subimage_ext():
 subimg=cv.CreateImageHeader((rctBlue[0],rctBlue[-1]),
 src3.depth,src3.nChannels)
 cv.SetData(subimg,cv.GetSubRect(src3,rctBlue).tostring())
 cv.AddS(subimg,offsetBlue,subimg)
 cv.ShowImage('Subsub-main',src3)
 cv.ShowImage('Subsub-sub',subimg)
 return 0
 
if __name__ == '__main__':
 offsetBlue=cv.Scalar(150)
 offsetGreen=cv.Scalar(0,150)
 offsetRed=cv.Scalar(0,0,150)
 rctBlue=(100,100,100,100)
 rctGreen=(200,100,100,100)
 rctRed=(150,200,100,100)
 src=cv.LoadImage('1.jpg')
 src2=cv.LoadImage('1.jpg')
 src3=cv.LoadImage('1.jpg')
 print roi()
 print subimage()
 print subimage_ext()
 cv.WaitKey(0)

Коментарии - Здесь я долго ковырялся, пытаясь сообразить как переделать Cкод на питон, т.к. все функции типа Create*Header и т.д. не имеют смысла в питоне, где мы не можем работать с указателями и т.д. + я обнаружил интересную систему работы присваивания, которая оказалась весьма удобной (изначально я думал придется пользоваться самым неудобным способом - SetImageROI (функция roi()))
()() "=" в питоне: Присваивание в Python происходит следующим образом: если присваеваемый объект является экземпляром таких типов как числа или строки, то действует семантика копирования, если же в правой части стоит экземпляр класса, список, словарь или тьюпл, то действует семантика указателей. ()()
Т.е. в функции subimage() я имею не несколько отдельных картинок, у каждой из которых есть своя область в памяти, на самом деле в моем распоряжении имена, ссылающиеся на область памяти, занятую данными загруженной картинкой (вот почему при сдвиге пикселей определенного канала меняется и главное изображение - данные то одни).
Ну и третья функция показывает, зачем же все-таки оставили хедеры - как результат мы получаем не новое имя, ссылающееся на часть данных картинки, а полностью самостоятельное изображение, имеющее свою область памяти.

Программа №3 - альфа-слияние
Процедура знакомая многим по работе в Photoshop
import cv

def main():
 cv.SetImageROI(src1,rectAlpha1)
 cv.SetImageROI(src2,rectBeta)
 cv.AddWeighted(src1,alpha,src2,beta,gamma,src1)
 cv.ResetImageROI(src1)
 cv.SetImageROI(src1,rectAlpha2)
 cv.AddWeighted(src1,alpha1,src2,beta1,gamma,src1)
 cv.ResetImageROI(src1)
 cv.SetImageROI(src1,rectAlpha3)
 cv.AddWeighted(src1,alpha,src2,beta,gamma1,src1)
 cv.ResetImageROI(src1)
 cv.ShowImage('Main2',src1)
 return 0

if __name__ == '__main__':
 src1=cv.LoadImage('1.jpg')
 src2=cv.LoadImage('2.bmp')
 rectBeta=(120,150,80,100)
 rectAlpha1=(0,0,80,100)
 rectAlpha2=(0,100,80,100)
 rectAlpha3=(0,200,80,100)
 gamma1=0.7
 alpha1=0.2
 beta1=0.8
 gamma=0.0
 alpha=0.5
 beta=0.5
 print main()
 cv.WaitKey(0)


Так же отсутствие функций Release*Header и т.д. натолкнули на мысль, что при работе с языком питон нет смысла в этих функциях, т.к. не работая напрямую с памятью мы там и не сорим, и все уберут за нас, поэтому и функции DestroyWindow или ReleaseCapture смысла тоже не имеют, и их оставили, наверное, только для сохранения общей структуры приложения на OpenCV, поэтому в этих приложениях я не использовал эти функции, так же как и создание именованного окна, оно создается автоматически при попытке отобразить изображение.


Комментариев нет:

Отправить комментарий