OpenCV Π΅ Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠ°, ΠΏΡΠ΅Π΄Π½Π°Π·Π½Π°ΡΠ΅Π½Π° Π·Π° ΠΏΡΠΎΠ΅ΠΊΡΠΈ Π·Π° ΠΊΠΎΠΌΠΏΡΡΡΡΠ½ΠΎ Π·ΡΠ΅Π½ΠΈΠ΅. Π’Ρ Π²Π΅ΡΠ΅ Π΅ Π½Π° ΠΎΠΊΠΎΠ»ΠΎ 20 Π³ΠΎΠ΄ΠΈΠ½ΠΈ. ΠΠ·ΠΏΠΎΠ»Π·Π²Π°Ρ
Π³ΠΎ Π² ΠΊΠΎΠ»Π΅ΠΆΠ° ΠΈ Π²ΡΠ΅ ΠΎΡΠ΅ Π³ΠΎ ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°ΠΌ Π·Π° ΠΌΠΎΠΈΡΠ΅ C++ ΠΈ Python ΠΏΡΠΎΠ΅ΠΊΡΠΈ, Π·Π°ΡΠΎΡΠΎ ΠΈΠΌΠ° Π΄ΠΎΠ±ΡΠ° ΠΏΠΎΠ΄Π΄ΡΡΠΆΠΊΠ° Π·Π° ΡΠ΅Π·ΠΈ Π΅Π·ΠΈΡΠΈ.
ΠΠΎ ΠΊΠΎΠ³Π°ΡΠΎ Π·Π°ΠΏΠΎΡΠ½Π°Ρ
Π΄Π° ΡΡΠ° ΠΈ ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°ΠΌ Go, ΡΠ΅ Π·Π°ΠΈΠ½ΡΠ΅ΡΠ΅ΡΡΠ²Π°Ρ
Π΄Π°Π»ΠΈ OpenCV ΠΌΠΎΠΆΠ΅ Π΄Π° ΡΠ΅ ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π° Π·Π° ΡΠ°Π±ΠΎΡΠ° Ρ ΡΠΎΠ·ΠΈ Π΅Π·ΠΈΠΊ. ΠΠΎ ΡΠΎΠ²Π° Π²ΡΠ΅ΠΌΠ΅ Π²Π΅ΡΠ΅ ΠΈΠΌΠ°ΡΠ΅ ΠΏΡΠΈΠΌΠ΅ΡΠΈ ΠΈ ΡΡΠΎΡΠΈ Π·Π° ΠΈΠ½ΡΠ΅Π³ΡΠ°ΡΠΈΡ, Π½ΠΎ ΠΌΠΈ ΡΠ΅ ΡΡΠΎΡΠΈΡ
Π° ΡΠ²ΡΡΠ΄Π΅ ΡΠ»ΠΎΠΆΠ½ΠΈ. ΠΠ°Π»ΠΊΠΎ ΠΏΠΎ-ΠΊΡΡΠ½ΠΎ ΠΏΠΎΠΏΠ°Π΄Π½Π°Ρ
Π½Π° ΠΎΠ±Π²ΠΈΠ²ΠΊΠ°, ΡΡΠ·Π΄Π°Π΄Π΅Π½Π° ΠΎΡ Π΅ΠΊΠΈΠΏΠ° Π½Π° The Hybrid Group. Π ΡΠ°Π·ΠΈ ΡΡΠ°ΡΠΈΡ ΡΠ΅ Π²ΠΈ ΠΏΠΎΠΊΠ°ΠΆΠ° ΠΊΠ°ΠΊ Π΄Π° Π·Π°ΠΏΠΎΡΠ½Π΅ΡΠ΅ Ρ GoCV, ΠΊΠ°ΡΠΎ ΡΠ°Π·ΡΠ°Π±ΠΎΡΠΈΡΠ΅ ΠΏΡΠΎΡΡΠ° ΡΠΈΡΡΠ΅ΠΌΠ° Π·Π° ΡΠ°Π·ΠΏΠΎΠ·Π½Π°Π²Π°Π½Π΅ Π½Π° Π»ΠΈΡΠ° Ρ Haar Cascades.
Skillbox ΠΏΡΠ΅ΠΏΠΎΡΡΡΠ²Π°: ΠΡΠ°ΠΊΡΠΈΡΠ΅ΡΠΊΠΈ ΠΊΡΡΡ
βΠ Π°Π·ΡΠ°Π±ΠΎΡΡΠΈΠΊ Π½Π° Python ΠΎΡ Π½ΡΠ»Π°ΡΠ°β .ΠΠ°ΠΏΠΎΠΌΠ½ΡΠΌΠ΅ Π²ΠΈ: Π·Π° Π²ΡΠΈΡΠΊΠΈ ΡΠΈΡΠ°ΡΠ΅Π»ΠΈ Π½Π° "Habr" - ΠΎΡΡΡΡΠΏΠΊΠ° ΠΎΡ 10 000 ΡΡΠ±Π»ΠΈ ΠΏΡΠΈ Π·Π°ΠΏΠΈΡΠ²Π°Π½Π΅ Π²ΡΠ² Π²ΡΠ΅ΠΊΠΈ ΠΊΡΡΡ Skillbox, ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°ΠΉΠΊΠΈ ΠΏΡΠΎΠΌΠΎΡΠΈΠΎΠ½Π°Π»Π½ΠΈΡ ΠΊΠΎΠ΄ Π½Π° "Habr".
ΠΠ°ΠΊΠ²ΠΎ ΡΠ΅ ΡΠ΅ ΠΈΠ·ΠΈΡΠΊΠ²Π°:
- ΠΡΠΈΠ²Π°ΠΌ;
- OpenCV (ΠΈΠ½ΡΡΠ°Π»Π°ΡΠΈΠΎΠ½Π½ΠΈ Π²ΡΡΠ·ΠΊΠΈ ΠΏΠΎ-Π΄ΠΎΠ»Ρ);
- ΡΠ΅Π± ΠΈΠ»ΠΈ ΠΎΠ±ΠΈΠΊΠ½ΠΎΠ²Π΅Π½Π° Π²ΠΈΠ΄Π΅ΠΎΠΊΠ°ΠΌΠ΅ΡΠ°.
ΠΠ½ΡΡΠ°Π»Π°ΡΠΈΡ
- Linux:
gocv.io/getting-started/linux - MacOS:
gocv.io/getting-started/macos - Windows:
gocv.io/getting-started/windows
ΠΡΠΈΠΌΠ΅Ρ 1
Π ΠΏΡΡΠ²ΠΈΡ ΠΏΡΠΈΠΌΠ΅Ρ ΡΠ΅ ΡΠ΅ ΠΎΠΏΠΈΡΠ°ΠΌΠ΅ Π΄Π° ΡΡΠ·Π΄Π°Π΄Π΅ΠΌ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅, ΠΊΠΎΠ΅ΡΠΎ ΠΎΡΠ²Π°ΡΡ ΠΏΡΠΎΠ·ΠΎΡΠ΅Ρ, ΠΏΠΎΠΊΠ°Π·Π²Π°Ρ Π²ΠΈΠ΄Π΅ΠΎ ΠΏΠΎΡΠΎΠΊ ΠΎΡ ΠΊΠ°ΠΌΠ΅ΡΠ°.
ΠΡΡΠ²ΠΎ ΡΡΡΠ±Π²Π° Π΄Π° ΠΈΠΌΠΏΠΎΡΡΠΈΡΠ°ΡΠ΅ Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠΈΡΠ΅, Π½Π΅ΠΎΠ±Ρ ΠΎΠ΄ΠΈΠΌΠΈ Π·Π° ΡΠ°Π±ΠΎΡΠ°.
Π²Π½ΠΎΡ (
"Π΄Π½Π΅Π²Π½ΠΈΠΊ"
βgocv.io/x/gocvβ
)
Π‘Π»Π΅Π΄ ΡΠΎΠ²Π° ΡΡΡΠ±Π²Π° Π΄Π° ΡΡΠ·Π΄Π°Π΄Π΅ΡΠ΅ ΠΎΠ±Π΅ΠΊΡ VideoCapture Ρ ΠΏΠΎΠΌΠΎΡΡΠ° Π½Π° ΡΡΠ½ΠΊΡΠΈΡΡΠ° VideoCaptureDevice. ΠΠΎΡΠ»Π΅Π΄Π½ΠΈΡΡ Π΄Π°Π²Π° Π²ΡΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡ Π·Π° Π·Π°ΡΠ½Π΅ΠΌΠ°Π½Π΅ Π½Π° Π²ΠΈΠ΄Π΅ΠΎ ΠΏΠΎΡΠΎΠΊ Ρ ΠΏΠΎΠΌΠΎΡΡΠ° Π½Π° ΠΊΠ°ΠΌΠ΅ΡΠ°. Π€ΡΠ½ΠΊΡΠΈΡΡΠ° ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π° ΡΡΠ»ΠΎ ΡΠΈΡΠ»ΠΎ ΠΊΠ°ΡΠΎ ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΡ (ΠΏΡΠ΅Π΄ΡΡΠ°Π²Π»ΡΠ²Π° ID Π½Π° ΡΡΡΡΠΎΠΉΡΡΠ²ΠΎΡΠΎ).
webcam, err := gocv.VideoCaptureDevice(0)
if err != nil { log.Fatalf(βerror opening web cam: %vβ, err)
}
defer webcam.Close()
Π‘Π΅Π³Π° ΡΡΡΠ±Π²Π° Π΄Π° ΡΡΠ·Π΄Π°Π΄Π΅ΠΌ n-ΠΌΠ΅ΡΠ½Π° ΠΌΠ°ΡΡΠΈΡΠ°. Π’ΠΎΠΉ ΡΠ΅ ΡΡΡ ΡΠ°Π½ΡΠ²Π° ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΡ, ΠΏΡΠΎΡΠ΅ΡΠ΅Π½ΠΈ ΠΎΡ ΠΊΠ°ΠΌΠ΅ΡΠ°ΡΠ°.
img := gocv.NewMat()
defer img.Close()
ΠΠ° Π΄Π° ΠΏΠΎΠΊΠ°ΠΆΠ΅ΡΠ΅ Π²ΠΈΠ΄Π΅ΠΎ ΠΏΠΎΡΠΎΠΊ, ΡΡΡΠ±Π²Π° Π΄Π° ΡΡΠ·Π΄Π°Π΄Π΅ΡΠ΅ ΠΏΡΠΎΠ·ΠΎΡΠ΅Ρ - ΡΠΎΠ²Π° ΠΌΠΎΠΆΠ΅ Π΄Π° ΡΡΠ°Π½Π΅ Ρ ΠΏΠΎΠΌΠΎΡΡΠ° Π½Π° ΡΡΠ½ΠΊΡΠΈΡΡΠ° NewWindow.
window := gocv.NewWindow(βwebcamwindowβ)
defer window.Close()
Π‘Π΅Π³Π° Π΄Π° ΠΏΡΠ΅ΠΌΠΈΠ½Π΅ΠΌ ΠΊΡΠΌ Π½Π°ΠΉ-ΠΈΠ½ΡΠ΅ΡΠ΅ΡΠ½Π°ΡΠ° ΡΠ°ΡΡ.
Π’ΡΠΉ ΠΊΠ°ΡΠΎ Π²ΠΈΠ΄Π΅ΠΎΡΠΎ Π΅ Π½Π΅ΠΏΡΠ΅ΠΊΡΡΠ½Π°Ρ ΠΏΠΎΡΠΎΠΊ ΠΎΡ ΡΠ°ΠΌΠΊΠΈ Π½Π° ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΡ, ΡΠ΅ ΡΡΡΠ±Π²Π° Π΄Π° ΡΡΠ·Π΄Π°Π΄Π΅ΠΌ Π±Π΅Π·ΠΊΡΠ°Π΅Π½ ΡΠΈΠΊΡΠ», Π·Π° Π΄Π° ΡΠ΅ΡΠ΅ΠΌ Π±Π΅Π·ΠΊΡΠ°ΠΉΠ½ΠΎ Π²ΠΈΠ΄Π΅ΠΎ ΠΏΠΎΡΠΎΠΊΠ° Π½Π° ΠΊΠ°ΠΌΠ΅ΡΠ°ΡΠ°. ΠΠ° Π΄Π° Π½Π°ΠΏΡΠ°Π²ΠΈΡΠ΅ ΡΠΎΠ²Π°, ΠΈΠΌΠ°ΡΠ΅ Π½ΡΠΆΠ΄Π° ΠΎΡ ΠΌΠ΅ΡΠΎΠ΄Π° Read ΠΎΡ ΡΠΈΠΏΠ° VideoCapture. Π’ΠΎΠΉ ΡΠ΅ ΠΎΡΠ°ΠΊΠ²Π° ΡΠΈΠΏ Mat (ΠΌΠ°ΡΡΠΈΡΠ°ΡΠ°, ΠΊΠΎΡΡΠΎ ΡΡΠ·Π΄Π°Π΄ΠΎΡ ΠΌΠ΅ ΠΏΠΎ-Π³ΠΎΡΠ΅), Π²ΡΡΡΠ°ΡΠ° Π±ΡΠ»Π΅Π²Π° ΡΡΠΎΠΉΠ½ΠΎΡΡ, ΠΏΠΎΠΊΠ°Π·Π²Π°ΡΠ° Π΄Π°Π»ΠΈ ΡΠ°ΠΌΠΊΠ°ΡΠ° ΠΎΡ VideoCapture Π΅ ΠΏΡΠΎΡΠ΅ΡΠ΅Π½Π° ΡΡΠΏΠ΅ΡΠ½ΠΎ ΠΈΠ»ΠΈ Π½Π΅.
for {
if ok := webcam.Read(&img); !ok || img.Empty( {
log.Println(βUnable to read from the webcamβ) continue
}
.
.
.
}
Π‘Π΅Π³Π° ΡΡΡΠ±Π²Π° Π΄Π° ΠΏΠΎΠΊΠ°ΠΆΠ΅ΡΠ΅ ΡΠ°ΠΌΠΊΠ°ΡΠ° Π² ΡΡΠ·Π΄Π°Π΄Π΅Π½ΠΈΡ ΠΏΡΠΎΠ·ΠΎΡΠ΅Ρ. ΠΠ°ΡΠ·Π°ΡΠ° Π·Π° ΠΏΡΠ΅ΠΌΠΈΠ½Π°Π²Π°Π½Π΅ ΠΊΡΠΌ ΡΠ»Π΅Π΄Π²Π°ΡΠΈΡ ΠΊΠ°Π΄ΡΡ Π΅ 50 ms.
ΠΏΡΠΎΠ·ΠΎΡΠ΅Ρ.IMShow(img)
window.WaitKey(50)
Π‘Π»Π΅Π΄ ΡΡΠ°ΡΡΠΈΡΠ°Π½Π΅ Π½Π° ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ΡΠΎ ΡΠ΅ ΡΠ΅ ΠΎΡΠ²ΠΎΡΠΈ ΠΏΡΠΎΠ·ΠΎΡΠ΅Ρ Ρ Π²ΠΈΠ΄Π΅ΠΎ ΠΏΠΎΡΠΎΠΊ ΠΎΡ ΠΊΠ°ΠΌΠ΅ΡΠ°ΡΠ°.
package main
import (
"log"
"gocv.io/x/gocv"
)
func main() {
webcam, err := gocv.VideoCaptureDevice(0)
if err != nil {
log.Fatalf("error opening device: %v", err)
}
defer webcam.Close()
img := gocv.NewMat()
defer img.Close()
window := gocv.NewWindow("webcamwindow")
defer window.Close()
for {
if ok := webcam.Read(&img); !ok || img.Empty() {
log.Println("Unable to read from the webcam")
continue
}
window.IMShow(img)
window.WaitKey(50)
}
}
ΠΡΠΈΠΌΠ΅Ρ 2
Π ΡΠΎΠ·ΠΈ ΠΏΡΠΈΠΌΠ΅Ρ Π½Π΅ΠΊΠ° ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°ΠΌΠ΅ ΠΏΡΠ΅Π΄ΠΈΡΠ½ΠΈΡ ΠΏΡΠΈΠΌΠ΅Ρ ΠΈ Π΄Π° ΠΈΠ·Π³ΡΠ°Π΄ΠΈΠΌ ΡΠΈΡΡΠ΅ΠΌΠ° Π·Π° ΡΠ°Π·ΠΏΠΎΠ·Π½Π°Π²Π°Π½Π΅ Π½Π° Π»ΠΈΡΠ°, Π±Π°Π·ΠΈΡΠ°Π½Π° Π½Π° ΠΊΠ°ΡΠΊΠ°Π΄ΠΈΡΠ΅ Π½Π° Π₯Π°Π°Ρ.
ΠΠ°ΡΠΊΠ°Π΄ΠΈΡΠ΅ Π½Π° Haar ΡΠ° ΠΊΠ°ΡΠΊΠ°Π΄Π½ΠΈ ΠΊΠ»Π°ΡΠΈΡΠΈΠΊΠ°ΡΠΎΡΠΈ, ΠΊΠΎΠΈΡΠΎ ΡΠ΅ ΠΎΠ±ΡΡΠ°Π²Π°Ρ Ρ ΠΏΠΎΠΌΠΎΡΡΠ° Π½Π° ΡΠ΅Ρ Π½ΠΈΠΊΠ°ΡΠ° Π½Π° Haar wavelet. Π’Π΅ Π°Π½Π°Π»ΠΈΠ·ΠΈΡΠ°Ρ ΠΏΠΈΠΊΡΠ΅Π»ΠΈΡΠ΅ Π² ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΠ΅ΡΠΎ, Π·Π° Π΄Π° ΠΎΡΠΊΡΠΈΡΡ ΠΎΠΏΡΠ΅Π΄Π΅Π»Π΅Π½ΠΈ Ρ Π°ΡΠ°ΠΊΡΠ΅ΡΠΈΡΡΠΈΠΊΠΈ. ΠΠ° Π΄Π° Π½Π°ΡΡΠΈΡΠ΅ ΠΏΠΎΠ²Π΅ΡΠ΅ Π·Π° Haar Cascades, ΠΌΠΎΠ»Ρ, ΡΠ»Π΅Π΄Π²Π°ΠΉΡΠ΅ Π²ΡΡΠ·ΠΊΠΈΡΠ΅ ΠΏΠΎ-Π΄ΠΎΠ»Ρ.
ΠΠ·ΡΠ΅Π³Π»Π΅ΡΠ΅ Π²Π΅ΡΠ΅ ΠΎΠ±ΡΡΠ΅Π½ΠΈ ΠΊΠ°ΡΠΊΠ°Π΄ΠΈ
ΠΠ° Π΄Π° Π½Π°ΠΏΡΠ°Π²ΠΈΡΠ΅ ΡΠΎΠ²Π°, ΡΡΡΠ±Π²Π° Π΄Π° ΡΡΠ·Π΄Π°Π΄Π΅ΡΠ΅ ΠΊΠ»Π°ΡΠΈΡΠΈΠΊΠ°ΡΠΎΡ ΠΈ Π΄Π° Π³ΠΎ Π·Π°Ρ ΡΠ°Π½ΠΈΡΠ΅ Ρ Π²Π΅ΡΠ΅ ΠΎΠ±ΡΡΠ΅Π½ ΡΠ°ΠΉΠ» (Π²ΡΡΠ·ΠΊΠ°ΡΠ° Π΅ Π΄Π°Π΄Π΅Π½Π° ΠΏΠΎ-Π³ΠΎΡΠ΅). ΠΠ΅ΡΠ΅ ΠΊΠ°ΡΠΈΡ ΡΠ°ΠΉΠ»Π° pencv_haarcascade_frontalface_default.xml Π² Π΄ΠΈΡΠ΅ΠΊΡΠΎΡΠΈΡΡΠ°, ΠΊΡΠ΄Π΅ΡΠΎ ΡΠ΅ Π½Π°ΠΌΠΈΡΠ° Π½Π°ΡΠ°ΡΠ° ΠΏΡΠΎΠ³ΡΠ°ΠΌΠ°.
harrcascade := βopencv_haarcascade_frontalface_default.xmlβclassifier := gocv.NewCascadeClassifier()classifier.Load(harrcascade)
defer classifier.Close()
ΠΠ° Π΄Π° ΠΎΡΠΊΡΠΈΠ΅ΡΠ΅ Π»ΠΈΡΠ° Π² ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΠ΅, ΡΡΡΠ±Π²Π° Π΄Π° ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°ΡΠ΅ ΠΌΠ΅ΡΠΎΠ΄Π°
for _, r := range rects {
fmt.Println(βdetectedβ, r)
gocv.Rectangle(&img, r, color, 2)
}
package main
import (
"fmt"
"image/color"
"log"
"gocv.io/x/gocv"
)
func main() {
webcam, err := gocv.VideoCaptureDevice(0)
if err != nil {
log.Fatalf("error opening web cam: %v", err)
}
defer webcam.Close()
img := gocv.NewMat()
defer img.Close()
window := gocv.NewWindow("webcamwindow")
defer window.Close()
harrcascade := "opencv_haarcascade_frontalface_default.xml"
classifier := gocv.NewCascadeClassifier()
classifier.Load(harrcascade)
defer classifier.Close()
color := color.RGBA{0, 255, 0, 0}
for {
if ok := webcam.Read(&img); !ok || img.Empty() {
log.Println("Unable to read from the device")
continue
}
rects := classifier.DetectMultiScale(img)
for _, r := range rects {
fmt.Println("detected", r)
gocv.Rectangle(&img, r, color, 3)
}
window.IMShow(img)
window.WaitKey(50)
}
}
Π... Π΄Π°, Π²ΡΠΈΡΠΊΠΎ ΡΠ΅ ΠΏΠΎΠ»ΡΡΠΈ! Π‘Π΅Π³Π° ΠΈΠΌΠ°ΠΌΠ΅ ΠΏΡΠΎΡΡΠ° ΡΠΈΡΡΠ΅ΠΌΠ° Π·Π° ΡΠ°Π·ΠΏΠΎΠ·Π½Π°Π²Π°Π½Π΅ Π½Π° Π»ΠΈΡΠ°, Π½Π°ΠΏΠΈΡΠ°Π½Π° Π½Π° Go. Π‘ΠΊΠΎΡΠΎ ΠΏΠ»Π°Π½ΠΈΡΠ°ΠΌ Π΄Π° ΠΏΡΠΎΠ΄ΡΠ»ΠΆΠ° Ρ ΡΠ΅Π·ΠΈ Π΅ΠΊΡΠΏΠ΅ΡΠΈΠΌΠ΅Π½ΡΠΈ ΠΈ Π΄Π° ΡΡΠ·Π΄Π°ΠΌ Π½ΠΎΠ²ΠΈ Π³ΠΎΡΠΈΠ½ΠΈ Π½Π΅ΡΠ°, ΠΊΠ°ΡΠΎ ΠΊΠΎΠΌΠ±ΠΈΠ½ΠΈΡΠ°ΠΌ Go ΠΈ OpenCV.
ΠΠΊΠΎ ΡΠ΅ ΠΈΠ½ΡΠ΅ΡΠ΅ΡΡΠ²Π°ΡΠ΅, ΠΌΠΎΠ»Ρ, ΠΎΡΠ΅Π½Π΅ΡΠ΅
ΠΠ»Π°Π³ΠΎΠ΄Π°ΡΡ Π²ΠΈ, ΡΠ΅ ΠΏΡΠΎΡΠ΅ΡΠΎΡ ΡΠ΅ ΡΡΠ°ΡΠΈΡΡΠ°!
Skillbox ΠΏΡΠ΅ΠΏΠΎΡΡΡΠ²Π°:
- ΠΠ²ΡΠ³ΠΎΠ΄ΠΈΡΠ΅Π½ ΠΏΡΠ°ΠΊΡΠΈΡΠ΅ΡΠΊΠΈ ΠΊΡΡΡ
βΠΠ· ΡΡΠΌ PRO ΡΠ΅Π± ΡΠ°Π·ΡΠ°Π±ΠΎΡΡΠΈΠΊβ .- ΠΠ±ΡΠ°Π·ΠΎΠ²Π°ΡΠ΅Π»Π΅Π½ ΠΎΠ½Π»Π°ΠΉΠ½ ΠΊΡΡΡ
βΠΡΠΎΡΠ΅ΡΠΈΡ Java ΡΠ°Π·ΡΠ°Π±ΠΎΡΡΠΈΠΊβ .- ΠΡΠ°ΠΊΡΠΈΡΠ΅ΡΠΊΠΈ Π³ΠΎΠ΄ΠΈΡΠ΅Π½ ΠΊΡΡΡ
βPHP ΡΠ°Π·ΡΠ°Π±ΠΎΡΡΠΈΠΊ ΠΎΡ 0 Π΄ΠΎ PROβ .
ΠΠ·ΡΠΎΡΠ½ΠΈΠΊ: www.habr.com